cpu: remove support for x86
This commit is contained in:
parent
b3eb9b8cf2
commit
fc45d7c5ac
@ -1,3 +0,0 @@
|
||||
MODULE = cpu
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
@ -1,3 +0,0 @@
|
||||
export USEMODULE += quad_math
|
||||
export USEMODULE += periph_common
|
||||
export USEPKG += tlsf
|
||||
@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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_H
|
||||
#define CPU_H
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "irq.h"
|
||||
#include "ucontext.h"
|
||||
#include "cpu_conf.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 beyond 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);
|
||||
|
||||
/**
|
||||
* @brief Prints the last instruction's address
|
||||
*/
|
||||
static inline void cpu_print_last_instruction(void)
|
||||
{
|
||||
void *p;
|
||||
__asm__("1: mov 1b, %0" : "=r" (p));
|
||||
printf("%p\n", p);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CPU_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.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
|
||||
*/
|
||||
|
||||
/**
|
||||
* stub for x86 periph configuration
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_H
|
||||
#define PERIPH_CPU_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Power management configuration
|
||||
* @{
|
||||
*/
|
||||
#define PROVIDES_PM_OFF
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH_CPU_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* 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 UCONTEXT_H
|
||||
#define UCONTEXT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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_info_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_info_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UCONTEXT_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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 X86_CMOS_H
|
||||
#define X86_CMOS_H
|
||||
|
||||
#include "x86_ports.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_CMOS_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* 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 X86_INTERRUPTS_H
|
||||
#define X86_INTERRUPTS_H
|
||||
|
||||
#include "ucontext.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 #X86_INT_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_INTERRUPTS_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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 X86_KERNEL_MEMORY_H
|
||||
#define X86_KERNEL_MEMORY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_KERNEL_MEMORY_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,183 +0,0 @@
|
||||
/*
|
||||
* 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 X86_MEMORY_H
|
||||
#define X86_MEMORY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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))
|
||||
|
||||
#ifdef DEBUG_READ_BEFORE_WRITE
|
||||
# define PT_HEAP_BIT PT_USR9
|
||||
#endif
|
||||
|
||||
#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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_MEMORY_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,423 +0,0 @@
|
||||
/*
|
||||
* 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 X86_PCI_H
|
||||
#define X86_PCI_H
|
||||
|
||||
#include "x86_pci_init.h"
|
||||
#include "x86_pic.h"
|
||||
|
||||
#include <machine/endian.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 (irq_is_in() == 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PCI_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef X86_PCI_INIT_H
|
||||
#define X86_PCI_INIT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* no PCI devices are implemented, yet */
|
||||
|
||||
void x86_init_pci_devices(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PCI_INIT_H */
|
||||
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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 X86_PCI_STRINGS_H
|
||||
#define X86_PCI_STRINGS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PCI_STRINGS_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* 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 X86_PIC_H
|
||||
#define X86_PIC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 (irq_is_in() == 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 #X86_INT_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PIC_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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 xtimer interface instead.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef X86_PIT_H
|
||||
#define X86_PIT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PIT_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,194 +0,0 @@
|
||||
/* 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 X86_PORTS_H
|
||||
#define X86_PORTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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:");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_PORTS_H */
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* 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 X86_REBOOT_H
|
||||
#define X86_REBOOT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "kernel_defines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_REBOOT_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,263 +0,0 @@
|
||||
/*
|
||||
* 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 X86_REGISTERS_H
|
||||
#define X86_REGISTERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 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 instructions cause
|
||||
#X86_INT_NM, SSE causes #X86_INT_UD) */
|
||||
#define CR0_TS (1u << 3) /**< 1 = task switched flag (causes #X86_INT_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 #X86_INT_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;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_REGISTERS_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,220 +0,0 @@
|
||||
/*
|
||||
* 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 X86_RTC_H
|
||||
#define X86_RTC_H
|
||||
|
||||
#include "msg.h"
|
||||
#include "x86_cmos.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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; /**< The full timestamp as a 64bit integer
|
||||
representation */
|
||||
} 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, `KERNEL_PID_UNDEF` 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 xtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, kernel_pid_t 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, `KERNEL_PID_UNDEF` 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 xtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, kernel_pid_t 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, `KERNEL_PID_UNDEF` 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 xtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_update(uint32_t msg_content, kernel_pid_t 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_RTC_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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 X86_THREADING_H
|
||||
#define X86_THREADING_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 irq_is_in() for the x86 port.
|
||||
*/
|
||||
extern bool x86_in_isr;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_THREADING_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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 X86_UART_H
|
||||
#define X86_UART_H
|
||||
|
||||
#include "x86_pic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_UART_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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 X86_VIDEORAM_H
|
||||
#define X86_VIDEORAM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Print out a single character on the graphical device.
|
||||
*
|
||||
* This method can be used before the UART system was initialized.
|
||||
*
|
||||
* Special characters 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* X86_VIDEORAM_H */
|
||||
|
||||
/** @} */
|
||||
@ -1,128 +0,0 @@
|
||||
#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");
|
||||
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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 "kernel_defines.h"
|
||||
#include "cpu.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 (pid_t) 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;
|
||||
}
|
||||
@ -1,432 +0,0 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "cpu.h"
|
||||
#include "sched.h"
|
||||
#include "thread.h"
|
||||
#include "ucontext.h"
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_memory.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_registers.h"
|
||||
#include "x86_threading.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;
|
||||
__attribute__((used)) 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_higher();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((used)) 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();
|
||||
}
|
||||
|
||||
__attribute__((used)) 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;
|
||||
}
|
||||
@ -1,494 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
|
||||
#ifndef DEBUG_READ_BEFORE_WRITE
|
||||
# define PT_HEAP_BITS (PT_P | PT_RW | PT_US | pt_xd)
|
||||
#else
|
||||
# define PT_HEAP_BITS (PT_HEAP_BIT | PT_RW | PT_US | pt_xd)
|
||||
#endif
|
||||
|
||||
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,
|
||||
},
|
||||
/* cppcheck-suppress duplicateExpression
|
||||
* it's for consistent look & feel */
|
||||
[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",
|
||||
(void *)&_kernel_memory_start, (void *)&_kernel_memory_end);
|
||||
printf(" .text: %p - %p\r\n", (void *)&_section_text_start, (void *)&_section_text_end);
|
||||
printf(" .rodata: %p - %p\r\n", (void *)&_section_rodata_start, (void *)&_section_rodata_end);
|
||||
printf(" .data: %p - %p\r\n", (void *)&_section_data_start, (void *)&_section_data_end);
|
||||
printf(" .bss: %p - %p\r\n", (void *)&_section_bss_start, (void *)&_section_bss_end);
|
||||
printf("Unmapped memory: %p - %p\r\n", (void *)&_kernel_memory_end, (void *)&_heap_start);
|
||||
printf("Heap start: %p\r\n", (void *)&_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();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_READ_BEFORE_WRITE
|
||||
uint32_t virtual_addr = cr2_read();
|
||||
uint64_t pte = x86_get_pte(virtual_addr);
|
||||
#endif
|
||||
|
||||
if (error_code & PF_EC_I) {
|
||||
puts("Page fault while fetching instruction.");
|
||||
x86_print_registers(orig_ctx, error_code);
|
||||
puts("Halting.");
|
||||
x86_hlt();
|
||||
}
|
||||
#ifdef DEBUG_READ_BEFORE_WRITE
|
||||
else if ((pte != NO_PTE) && !(pte & PT_P) && (pte & PT_HEAP_BIT)) {
|
||||
/* mark as present */
|
||||
TEMP_PAGE.indices[(virtual_addr >> 12) % 512] |= PT_P;
|
||||
__asm__ volatile ("invlpg (%0)" :: "r"(virtual_addr));
|
||||
|
||||
/* initialize for easier debugging */
|
||||
uint32_t *p = (uint32_t *) (virtual_addr & ~0xfff);
|
||||
for (unsigned i = 0; i < 0x1000 / 4; ++i) {
|
||||
const union {
|
||||
char str_value[4];
|
||||
uint32_t int_value;
|
||||
} debug_init = { .str_value = "RIOT" };
|
||||
*p++ = debug_init.int_value;
|
||||
}
|
||||
|
||||
/* print a warning if the page was read before written */
|
||||
if (!(error_code & PF_EC_W)) {
|
||||
unsigned long *sp = (void *) orig_ctx->sp; /* ip, cs, flags */
|
||||
printf("DEBUG: Read before write on heap address 0x%08x (physical: 0x%016llx) at 0x%08lx.\n",
|
||||
virtual_addr, pte & PT_ADDR_MASK, sp[0]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
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_pagefault_handler();
|
||||
init_free_pages();
|
||||
|
||||
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);
|
||||
}
|
||||
@ -1,333 +0,0 @@
|
||||
/*
|
||||
* 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, (unsigned) ((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);
|
||||
|
||||
/* cppcheck-suppress memleakOnRealloc
|
||||
* (reason: TODO this is a bug) */
|
||||
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;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
@ -1,409 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
#if 0 /* 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;
|
||||
#endif
|
||||
}
|
||||
|
||||
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 = irq_disable();
|
||||
|
||||
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));
|
||||
|
||||
irq_restore(old_status);
|
||||
}
|
||||
|
||||
void x86_pic_enable_irq(unsigned num)
|
||||
{
|
||||
unsigned old_status = irq_disable();
|
||||
|
||||
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));
|
||||
|
||||
irq_restore(old_status);
|
||||
}
|
||||
|
||||
void x86_pic_disable_irq(unsigned num)
|
||||
{
|
||||
unsigned old_status = irq_disable();
|
||||
|
||||
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));
|
||||
|
||||
irq_restore(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;
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* 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 = irq_disable();
|
||||
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;
|
||||
irq_restore(old_flags);
|
||||
return lohi;
|
||||
}
|
||||
|
||||
void x86_pit_set2(unsigned channel, unsigned mode, uint16_t max)
|
||||
{
|
||||
unsigned old_flags = irq_disable();
|
||||
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);
|
||||
irq_restore(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;
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 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>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_reboot.h"
|
||||
#include "periph/pm.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;
|
||||
|
||||
void pm_reboot(void)
|
||||
{
|
||||
__asm__ volatile ("cli");
|
||||
if (!reboot_twice) {
|
||||
reboot_twice = true;
|
||||
if (reboot_fun) {
|
||||
reboot_fun();
|
||||
}
|
||||
}
|
||||
x86_kbc_reboot();
|
||||
}
|
||||
|
||||
void pm_off(void)
|
||||
{
|
||||
x86_shutdown();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1,298 +0,0 @@
|
||||
/*
|
||||
* 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 kernel_pid_t alarm_pid = KERNEL_PID_UNDEF, periodic_pid = KERNEL_PID_UNDEF, update_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
static void alarm_callback_default(uint8_t reg_c)
|
||||
{
|
||||
if (alarm_pid != KERNEL_PID_UNDEF) {
|
||||
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 != KERNEL_PID_UNDEF) {
|
||||
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 != KERNEL_PID_UNDEF) {
|
||||
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 = irq_disable();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
irq_restore(old_status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, kernel_pid_t target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = irq_disable();
|
||||
bool result;
|
||||
if (target_pid == KERNEL_PID_UNDEF) {
|
||||
result = true;
|
||||
alarm_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
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 == KERNEL_PID_UNDEF;
|
||||
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);
|
||||
irq_restore(old_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, kernel_pid_t target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = irq_disable();
|
||||
bool result;
|
||||
if (target_pid == KERNEL_PID_UNDEF || hz == RTC_REG_A_HZ_OFF) {
|
||||
result = true;
|
||||
periodic_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
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 == KERNEL_PID_UNDEF;
|
||||
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);
|
||||
irq_restore(old_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_update(uint32_t msg_content, kernel_pid_t target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = irq_disable();
|
||||
bool result;
|
||||
if (target_pid == KERNEL_PID_UNDEF) {
|
||||
result = true;
|
||||
update_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_UPDATE);
|
||||
}
|
||||
else {
|
||||
result = allow_replace || update_pid == KERNEL_PID_UNDEF;
|
||||
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);
|
||||
irq_restore(old_status);
|
||||
return result;
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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_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 <tlsf-malloc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "kernel_init.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_create_with_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_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();
|
||||
}
|
||||
@ -1,236 +0,0 @@
|
||||
/*
|
||||
* 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 "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 kernel_pid_t fpu_owner = KERNEL_PID_UNDEF;
|
||||
|
||||
//static ucontext_t *cur_ctx, *isr_ctx;
|
||||
|
||||
static struct x86_fxsave initial_fpu_state;
|
||||
|
||||
int irq_is_in(void)
|
||||
{
|
||||
return x86_in_isr;
|
||||
}
|
||||
|
||||
unsigned irq_disable(void)
|
||||
{
|
||||
unsigned long eflags = x86_pushf_cli();
|
||||
return (eflags & X86_IF) != 0;
|
||||
}
|
||||
|
||||
unsigned irq_enable(void)
|
||||
{
|
||||
unsigned long eflags;
|
||||
__asm__ volatile ("pushf; pop %0; sti" : "=g"(eflags));
|
||||
return (eflags & X86_IF) != 0;
|
||||
}
|
||||
|
||||
void irq_restore(unsigned state)
|
||||
{
|
||||
if (state) {
|
||||
__asm__ volatile ("sti");
|
||||
}
|
||||
else {
|
||||
__asm__ volatile ("cli");
|
||||
}
|
||||
}
|
||||
|
||||
int irq_is_in(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_higher(void)
|
||||
{
|
||||
if (x86_in_isr) {
|
||||
isr_thread_yield();
|
||||
}
|
||||
|
||||
unsigned old_intr = irq_disable();
|
||||
|
||||
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);
|
||||
|
||||
irq_restore(old_intr);
|
||||
}
|
||||
|
||||
void *thread_arch_isr_stack_pointer(void)
|
||||
{
|
||||
return isr_context.uc_stack.ss_sp;
|
||||
}
|
||||
|
||||
void *thread_arch_isr_stack_start(void)
|
||||
{
|
||||
return isr_stack;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
irq_disable();
|
||||
|
||||
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(thread_task_func_t task_func, void *arg, 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;
|
||||
p->__fxsave = initial_fpu_state;
|
||||
makecontext(p, (makecontext_fun_t) task_func, 1, arg);
|
||||
|
||||
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 != KERNEL_PID_UNDEF) {
|
||||
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)
|
||||
{
|
||||
irq_disable();
|
||||
if (fpu_owner == sched_active_pid) {
|
||||
fpu_owner = KERNEL_PID_UNDEF;
|
||||
}
|
||||
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);
|
||||
__asm__ volatile ("fxsave (%0)" :: "r"(&initial_fpu_state));
|
||||
|
||||
DEBUG("Threading initialized\n");
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
}
|
||||
|
||||
void x86_init_uart(void)
|
||||
{
|
||||
}
|
||||
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user