From a8968330aff45e0b8cf278f49fa337d5fcb9bfd8 Mon Sep 17 00:00:00 2001 From: Alex AUVOLAT Date: Fri, 28 Mar 2014 17:09:15 +0100 Subject: Import and compile code for article 6.5 --- sos-code-article6.5/hwcore/cpu_context.c | 407 +++++++++++++++++++++ sos-code-article6.5/hwcore/cpu_context.h | 270 ++++++++++++++ sos-code-article6.5/hwcore/cpu_context_switch.S | 128 +++++++ sos-code-article6.5/hwcore/exception.c | 167 +++++++++ sos-code-article6.5/hwcore/exception.h | 83 +++++ sos-code-article6.5/hwcore/exception_wrappers.S | 221 +++++++++++ sos-code-article6.5/hwcore/gdt.c | 152 ++++++++ sos-code-article6.5/hwcore/gdt.h | 42 +++ sos-code-article6.5/hwcore/i8254.c | 79 ++++ sos-code-article6.5/hwcore/i8254.h | 35 ++ sos-code-article6.5/hwcore/i8259.c | 79 ++++ sos-code-article6.5/hwcore/i8259.h | 40 ++ sos-code-article6.5/hwcore/idt.c | 159 ++++++++ sos-code-article6.5/hwcore/idt.h | 84 +++++ sos-code-article6.5/hwcore/ioports.h | 46 +++ sos-code-article6.5/hwcore/irq.c | 100 +++++ sos-code-article6.5/hwcore/irq.h | 97 +++++ sos-code-article6.5/hwcore/irq_wrappers.S | 220 +++++++++++ sos-code-article6.5/hwcore/paging.c | 465 ++++++++++++++++++++++++ sos-code-article6.5/hwcore/paging.h | 132 +++++++ sos-code-article6.5/hwcore/segment.h | 70 ++++ 21 files changed, 3076 insertions(+) create mode 100644 sos-code-article6.5/hwcore/cpu_context.c create mode 100644 sos-code-article6.5/hwcore/cpu_context.h create mode 100644 sos-code-article6.5/hwcore/cpu_context_switch.S create mode 100644 sos-code-article6.5/hwcore/exception.c create mode 100644 sos-code-article6.5/hwcore/exception.h create mode 100644 sos-code-article6.5/hwcore/exception_wrappers.S create mode 100644 sos-code-article6.5/hwcore/gdt.c create mode 100644 sos-code-article6.5/hwcore/gdt.h create mode 100644 sos-code-article6.5/hwcore/i8254.c create mode 100644 sos-code-article6.5/hwcore/i8254.h create mode 100644 sos-code-article6.5/hwcore/i8259.c create mode 100644 sos-code-article6.5/hwcore/i8259.h create mode 100644 sos-code-article6.5/hwcore/idt.c create mode 100644 sos-code-article6.5/hwcore/idt.h create mode 100644 sos-code-article6.5/hwcore/ioports.h create mode 100644 sos-code-article6.5/hwcore/irq.c create mode 100644 sos-code-article6.5/hwcore/irq.h create mode 100644 sos-code-article6.5/hwcore/irq_wrappers.S create mode 100644 sos-code-article6.5/hwcore/paging.c create mode 100644 sos-code-article6.5/hwcore/paging.h create mode 100644 sos-code-article6.5/hwcore/segment.h (limited to 'sos-code-article6.5/hwcore') diff --git a/sos-code-article6.5/hwcore/cpu_context.c b/sos-code-article6.5/hwcore/cpu_context.c new file mode 100644 index 0000000..770452d --- /dev/null +++ b/sos-code-article6.5/hwcore/cpu_context.c @@ -0,0 +1,407 @@ +/* Copyright (C) 2005 David Decotigny + Copyright (C) 2000-2004, The KOS team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ + + +#include +#include +#include +#include +#include + +#include "cpu_context.h" + + +/** + * Here is the definition of a CPU context for IA32 processors. This + * is a SOS convention, not a specification given by the IA32 + * spec. However there is a strong constraint related to the x86 + * interrupt handling specification: the top of the stack MUST be + * compatible with the 'iret' instruction, ie there must be the + * err_code (might be 0), eip, cs and eflags of the destination + * context in that order (see Intel x86 specs vol 3, figure 5-4). + * + * @note IMPORTANT: This definition MUST be consistent with the way + * the registers are stored on the stack in + * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above. + */ +struct sos_cpu_state { + /* (Lower addresses) */ + + /* These are SOS convention */ + sos_ui16_t gs; + sos_ui16_t fs; + sos_ui16_t es; + sos_ui16_t ds; + sos_ui16_t cpl0_ss; /* This is ALWAYS the Stack Segment of the + Kernel context (CPL0) of the interrupted + thread, even for a user thread */ + sos_ui16_t alignment_padding; /* unused */ + sos_ui32_t eax; + sos_ui32_t ebx; + sos_ui32_t ecx; + sos_ui32_t edx; + sos_ui32_t esi; + sos_ui32_t edi; + sos_ui32_t ebp; + + /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */ + sos_ui32_t error_code; + sos_vaddr_t eip; + sos_ui32_t cs; /* 32bits according to the specs ! However, the CS + register is really 16bits long */ + sos_ui32_t eflags; + + /* (Higher addresses) */ +} __attribute__((packed)); + + +/** + * The CS value pushed on the stack by the CPU upon interrupt, and + * needed by the iret instruction, is 32bits long while the real CPU + * CS register is 16bits only: this macro simply retrieves the CPU + * "CS" register value from the CS value pushed on the stack by the + * CPU upon interrupt. + * + * The remaining 16bits pushed by the CPU should be considered + * "reserved" and architecture dependent. IMHO, the specs don't say + * anything about them. Considering that some architectures generate + * non-zero values for these 16bits (at least Cyrix), we'd better + * ignore them. + */ +#define GET_CPU_CS_REGISTER_VALUE(pushed_ui32_cs_value) \ + ( (pushed_ui32_cs_value) & 0xffff ) + + +/** + * Structure of an interrupted Kernel thread's context + */ +struct sos_cpu_kstate +{ + struct sos_cpu_state regs; +} __attribute__((packed)); + + +/** + * THE main operation of a kernel thread. This routine calls the + * kernel thread function start_func and calls exit_func when + * start_func returns. + */ +static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, + sos_ui32_t start_arg, + sos_cpu_kstate_function_arg1_t *exit_func, + sos_ui32_t exit_arg) + __attribute__((noreturn)); + +static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, + sos_ui32_t start_arg, + sos_cpu_kstate_function_arg1_t *exit_func, + sos_ui32_t exit_arg) +{ + start_func(start_arg); + exit_func(exit_arg); + + SOS_ASSERT_FATAL(! "The exit function of the thread should NOT return !"); + for(;;); +} + + +sos_ret_t sos_cpu_kstate_init(struct sos_cpu_state **ctxt, + sos_cpu_kstate_function_arg1_t *start_func, + sos_ui32_t start_arg, + sos_vaddr_t stack_bottom, + sos_size_t stack_size, + sos_cpu_kstate_function_arg1_t *exit_func, + sos_ui32_t exit_arg) +{ + /* We are initializing a Kernel thread's context */ + struct sos_cpu_kstate *kctxt; + + /* This is a critical internal function, so that it is assumed that + the caller knows what he does: we legitimally assume that values + for ctxt, start_func, stack_* and exit_func are allways VALID ! */ + + /* Setup the stack. + * + * On x86, the stack goes downward. Each frame is configured this + * way (higher addresses first): + * + * - (optional unused space. As of gcc 3.3, this space is 24 bytes) + * - arg n + * - arg n-1 + * - ... + * - arg 1 + * - return instruction address: The address the function returns to + * once finished + * - local variables + * + * The remaining of the code should be read from the end upward to + * understand how the processor will handle it. + */ + + sos_vaddr_t tmp_vaddr = stack_bottom + stack_size; + sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr; + + /* If needed, poison the stack */ +#ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS + memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, stack_size); +#elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) + sos_cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size); +#endif + + /* Simulate a call to the core_routine() function: prepare its + arguments */ + *(--stack) = exit_arg; + *(--stack) = (sos_ui32_t)exit_func; + *(--stack) = start_arg; + *(--stack) = (sos_ui32_t)start_func; + *(--stack) = 0; /* Return address of core_routine => force page fault */ + + /* + * Setup the initial context structure, so that the CPU will execute + * the function core_routine() once this new context has been + * restored on CPU + */ + + /* Compute the base address of the structure, which must be located + below the previous elements */ + tmp_vaddr = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate); + kctxt = (struct sos_cpu_kstate*)tmp_vaddr; + + /* Initialize the CPU context structure */ + memset(kctxt, 0x0, sizeof(struct sos_cpu_kstate)); + + /* Tell the CPU context structure that the first instruction to + execute will be that of the core_routine() function */ + kctxt->regs.eip = (sos_ui32_t)core_routine; + + /* Setup the segment registers */ + kctxt->regs.cs + = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE); /* Code */ + kctxt->regs.ds + = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */ + kctxt->regs.es + = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */ + kctxt->regs.cpl0_ss + = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Stack */ + /* fs and gs unused for the moment. */ + + /* The newly created context is initially interruptible */ + kctxt->regs.eflags = (1 << 9); /* set IF bit */ + + /* Finally, update the generic kernel/user thread context */ + *ctxt = (struct sos_cpu_state*) kctxt; + + return SOS_OK; +} + + +#if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) +void +sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, + sos_vaddr_t stack_bottom, + sos_size_t stack_size) +{ + sos_size_t poison_size = SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW; + if (poison_size > stack_size) + poison_size = stack_size; + + memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, poison_size); +} + + +void +sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, + sos_vaddr_t stack_bottom, + sos_size_t stack_size) +{ + unsigned char *c; + int i; + + /* On SOS, "ctxt" corresponds to the address of the esp register of + the saved context in Kernel mode (always, even for the interrupted + context of a user thread). Here we make sure that this stack + pointer is within the allowed stack area */ + SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) >= stack_bottom); + SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate) + <= stack_bottom + stack_size); + + /* Check that the bottom of the stack has not been altered */ + for (c = (unsigned char*) stack_bottom, i = 0 ; + (i < SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) && (i < stack_size) ; + c++, i++) + { + SOS_ASSERT_FATAL(SOS_CPU_STATE_STACK_POISON == *c); + } +} +#endif + + +/* ======================================================================= + * Public Accessor functions + */ + + +sos_vaddr_t sos_cpu_context_get_PC(const struct sos_cpu_state *ctxt) +{ + SOS_ASSERT_FATAL(NULL != ctxt); + + /* This is the PC of the interrupted context (ie kernel or user + context). */ + return ctxt->eip; +} + + +sos_vaddr_t sos_cpu_context_get_SP(const struct sos_cpu_state *ctxt) +{ + SOS_ASSERT_FATAL(NULL != ctxt); + + /* On SOS, "ctxt" corresponds to the address of the esp register of + the saved context in Kernel mode (always, even for the interrupted + context of a user thread). */ + return (sos_vaddr_t)ctxt; +} + + +void sos_cpu_context_dump(const struct sos_cpu_state *ctxt) +{ + char buf[128]; + snprintf(buf, sizeof(buf), + "CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", + (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags, + (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds, + (unsigned)ctxt->cpl0_ss, + (unsigned)ctxt->error_code); + sos_bochs_putstring(buf); sos_bochs_putstring("\n"); + sos_x86_videomem_putstring(23, 0, + SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY, + buf); +} + + +/* ======================================================================= + * Public Accessor functions TO BE USED ONLY BY Exception handlers + */ + + +sos_ui32_t sos_cpu_context_get_EX_info(const struct sos_cpu_state *ctxt) +{ + SOS_ASSERT_FATAL(NULL != ctxt); + return ctxt->error_code; +} + + +sos_vaddr_t +sos_cpu_context_get_EX_faulting_vaddr(const struct sos_cpu_state *ctxt) +{ + sos_ui32_t cr2; + + /* + * See Intel Vol 3 (section 5.14): the address of the faulting + * virtual address of a page fault is stored in the cr2 + * register. + * + * Actually, we do not store the cr2 register in a saved + * kernel thread's context. So we retrieve the cr2's value directly + * from the processor. The value we retrieve in an exception handler + * is actually the correct one because an exception is synchronous + * with the code causing the fault, and cannot be interrupted since + * the IDT entries in SOS are "interrupt gates" (ie IRQ are + * disabled). + */ + asm volatile ("movl %%cr2, %0" + :"=r"(cr2) + : ); + + return cr2; +} + + +/* ======================================================================= + * Backtrace facility. To be used for DEBUGging purpose ONLY. + */ + + +sos_ui32_t sos_backtrace(const struct sos_cpu_state *cpu_state, + sos_ui32_t max_depth, + sos_vaddr_t stack_bottom, + sos_size_t stack_size, + sos_backtrace_callback_t * backtracer, + void *custom_arg) +{ + int depth; + sos_vaddr_t callee_PC, caller_frame; + + /* + * Layout of a frame on the x86 (compiler=gcc): + * + * funcA calls funcB calls funcC + * + * .... + * funcB Argument 2 + * funcB Argument 1 + * funcA Return eip + * frameB: funcA ebp (ie previous stack frame) + * .... + * (funcB local variables) + * .... + * funcC Argument 2 + * funcC Argument 1 + * funcB Return eip + * frameC: funcB ebp (ie previous stack frame == A0) <---- a frame address + * .... + * (funcC local variables) + * .... + * + * The presence of "ebp" on the stack depends on 2 things: + * + the compiler is gcc + * + the source is compiled WITHOUT the -fomit-frame-pointer option + * In the absence of "ebp", chances are high that the value pushed + * at that address is outside the stack boundaries, meaning that the + * function will return -SOS_ENOSUP. + */ + + if (cpu_state) + { + callee_PC = cpu_state->eip; + caller_frame = cpu_state->ebp; + } + else + { + /* Skip the sos_backtrace() frame */ + callee_PC = (sos_vaddr_t)__builtin_return_address(0); + caller_frame = (sos_vaddr_t)__builtin_frame_address(1); + } + + for(depth=0 ; depth < max_depth ; depth ++) + { + /* Call the callback */ + backtracer(callee_PC, caller_frame + 8, depth, custom_arg); + + /* If the frame address is funky, don't go further */ + if ( (caller_frame < stack_bottom) + || (caller_frame + 4 >= stack_bottom + stack_size) ) + return depth; + + /* Go to caller frame */ + callee_PC = *((sos_vaddr_t*) (caller_frame + 4)); + caller_frame = *((sos_vaddr_t*) caller_frame); + } + + return depth; +} diff --git a/sos-code-article6.5/hwcore/cpu_context.h b/sos-code-article6.5/hwcore/cpu_context.h new file mode 100644 index 0000000..43b3718 --- /dev/null +++ b/sos-code-article6.5/hwcore/cpu_context.h @@ -0,0 +1,270 @@ +/* Copyright (C) 2005 David Decotigny + Copyright (C) 2000-2004, The KOS team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_CPUCTXT_H_ +#define _SOS_CPUCTXT_H_ + + +/** + * @file cpu_context.h + * + * Low level API to manage kernel and user thread CPU contexts. Should + * be some kind of architecture-independent. + */ + +#include +#include + + +/** + * Opaque structure storing the CPU context of an inactive kernel or + * user thread, as saved by the low level primitives below or by the + * interrupt/exception handlers. + * + * @note This is an (architecture-independent) forward declaration: + * see cpu_context.c and the *.S files for its + * (architecture-dependent) definition. + */ +struct sos_cpu_state; + + +/** + * The type of the functions passed as arguments to the Kernel thread + * related functions. + */ +typedef void (sos_cpu_kstate_function_arg1_t(sos_ui32_t arg1)); + + +/** + * Function to create an initial context for a kernel thread starting + * its execution at function start_func with the argument initial_arg, + * and having the stack defined by stack_bottom/stack_size. When the + * start_func function returns, the function exit_func is called with + * argument exit_arg. + * + * @param kctxt The kernel thread CPU context to initialize. The + * address of the newly-initialized struct sos_cpu_state will be + * stored in this variable. The contents of this struct sos_cpu_state + * are actually located /inside/ the stack. + * + * @param start_func The address of the first instruction that will be + * executed when this context will be first transferred on + * CPU. Practically speaking, this is the address of a function that + * is assumed to take 1 argument. + * + * @param start_arg The value that will be passed as the argument to + * start_func when the thread starts. The stack will be setup + * accordingly to simulate a real call to the function and really + * passing this arguement. + * + * @param stack_bottom The lowest address of the stack. + * + * @param stack_size The size of the stack. + * + * @param exit_func The address of the instruction executed after the + * function start_func has returned. This function takes 1 parameter + * as argument: exit_arg. + * + * @param exit_arg The argument passed to the function exit_func. + * + * @note the newly created context is INTERRUPTIBLE by default ! + */ +sos_ret_t sos_cpu_kstate_init(struct sos_cpu_state **kctxt, + sos_cpu_kstate_function_arg1_t *start_func, + sos_ui32_t start_arg, + sos_vaddr_t stack_bottom, + sos_size_t stack_size, + sos_cpu_kstate_function_arg1_t *exit_func, + sos_ui32_t exit_arg); + + +/** + * Function that performs an immediate context-switch from one + * kernel/user thread to another one. It stores the current executing + * context in from_ctxt, and restores to_context on CPU. + * + * @param from_ctxt The address of the struct sos_cpu_state will be + * stored in this variable. Must NOT be NULL. + * + * @param to_ctxt The CPU will resume its execution with the struct + * sos_cpu_state located at this address. Must NOT be NULL. + */ +void sos_cpu_context_switch(struct sos_cpu_state **from_ctxt, + struct sos_cpu_state *to_ctxt); + + +/* + * Switch to the new given context (of a kernel/user thread) without + * saving the old context (of another kernel/user thread), and call + * the function reclaiming_func passing it the recalining_arg + * argument. The reclaining function is called from within the stack + * of the new context, so that it can (among other things) safely + * destroy the stack of the former context. + * + * @param switch_to_ctxt The context that will be restored on the CPU + * + * @param reclaiming_func The address of the function that will be + * called after having changed the stack, but before restoring the CPU + * context to switch_to_ctxt. + */ +void +sos_cpu_context_exit_to(struct sos_cpu_state *switch_to_ctxt, + sos_cpu_kstate_function_arg1_t *reclaiming_func, + sos_ui32_t reclaiming_arg) __attribute__((noreturn)); + +/* ======================================================================= + * Public Accessor functions + */ + + +/** + * Return Program Counter stored in the saved kernel/user context + */ +sos_vaddr_t sos_cpu_context_get_PC(const struct sos_cpu_state *ctxt); + + +/** + * Return Stack Pointer stored in the saved kernel/user context + */ +sos_vaddr_t sos_cpu_context_get_SP(const struct sos_cpu_state *ctxt); + + +/** + * Dump the contents of the CPU context (bochs + x86_videomem) + */ +void sos_cpu_context_dump(const struct sos_cpu_state *ctxt); + + +/* ======================================================================= + * Public Accessor functions TO BE USED ONLY BY Exception handlers + */ + + +/** + * Return the argument passed by the CPU upon exception, as stored in the + * saved context + */ +sos_ui32_t sos_cpu_context_get_EX_info(const struct sos_cpu_state *ctxt); + + +/** + * Return the faulting address of the exception + */ +sos_vaddr_t +sos_cpu_context_get_EX_faulting_vaddr(const struct sos_cpu_state *ctxt); + + +/* ======================================================================= + * Macros controlling stack poisoning. + * Stack poisoning can be used to detect: + * - unitialized local variables + * - when the thread might have gone too deep in the stack + */ +/** The signature of the poison */ +#define SOS_CPU_STATE_STACK_POISON 0xa5 + +/** + * When set, mean that the whole stack is poisoned to detect use of + * unititialized variables + */ +#define SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS +/* #undef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS */ + +/** + * When set, mean that the bottom of the stack is poisoned to detect + * probable stack overflow. Its value indicates the number of bytes + * used for this detection. + */ +#define SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW 64 +/* #undef SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW */ + +#if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) +void +sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, + sos_vaddr_t kernel_stack_bottom, + sos_size_t kernel_stack_size); +void sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt, + sos_vaddr_t kernel_stack_bottom, + sos_size_t kernel_stack_size); +#else +# define sos_cpu_state_prepare_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \ + ({ /* nop */ }) +# define sos_cpu_state_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \ + ({ /* nop */ }) +#endif + + +/* ======================================================================= + * Backtrace facility. To be used for DEBUGging purpose ONLY. + */ + + +/** + * The function called at each step of the backtrace iterations + * + * @param PC The address of the next instruction of the function that + * will be executed + * + * @param params The address of the array of the parameteres that have + * been passed to the function considered + * + * @param depth The index of the iteration (ie the depth of the + * current frame into the stack) + * + * @param custom_arg Whatever you want: this is the argument passed as + * custom_arg to sos_backtrace() + */ +typedef void (sos_backtrace_callback_t)(sos_vaddr_t PC, + sos_vaddr_t params, + sos_ui32_t depth, + void *custom_arg); + + +/** + * Call the backtracer callback on each frame stored in the cpu_state + * + * @param cpu_state The CPU context we want to explore. MUST be the + * context of a thread in Kernel mode, or NULL. When NULL: backtrace + * the current CPU context. + * + * @param max_depth The maximum number of frames to explore + * + * @param stack_bottom The lower boundary of the stack. This is used + * to make sure that the frame addresses fit inside the stack + * boudaries (ie are potentially correct). + * + * @param stack_size The size of the stack. Same comment. + * + * @param backtracer The function to call to handle the frame for each + * iteration + * + * @param custom_arg The arg passed as custom_arg to the backtracer + * + * @return The number of frames explored. + * + * @note Might be inaccurate when gcc's -fomit-frame-pointer has been + * used. + */ +sos_ui32_t sos_backtrace(const struct sos_cpu_state *cpu_state, + sos_ui32_t max_depth, + sos_vaddr_t stack_bottom, + sos_size_t stack_size, + sos_backtrace_callback_t * backtracer, + void *custom_arg); + +#endif /* _SOS_CPUCTXT_H_ */ diff --git a/sos-code-article6.5/hwcore/cpu_context_switch.S b/sos-code-article6.5/hwcore/cpu_context_switch.S new file mode 100644 index 0000000..4499c91 --- /dev/null +++ b/sos-code-article6.5/hwcore/cpu_context_switch.S @@ -0,0 +1,128 @@ +/* Copyright (C) 2005 David Decotigny + Copyright (C) 2000-2004, The KOS team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#define ASM_SOURCE 1 + + +.file "cpu_context_switch.S" + +.text + + +.globl sos_cpu_context_switch +.type sos_cpu_context_switch, @function +sos_cpu_context_switch: + // arg2= to_context -- esp+64 + // arg1= from_context -- esp+60 + // caller ip -- esp+56 + pushf // (eflags) esp+52 + pushl %cs // (cs) esp+48 + pushl $resume_pc // (ip) esp+44 + pushl $0 // (error code) esp+40 + pushl %ebp // esp+36 + pushl %edi // esp+32 + pushl %esi // esp+28 + pushl %edx // esp+24 + pushl %ecx // esp+20 + pushl %ebx // esp+16 + pushl %eax // esp+12 + subl $2, %esp // (alignment) esp+10 + pushw %ss // esp+8 + pushw %ds // esp+6 + pushw %es // esp+4 + pushw %fs // esp+2 + pushw %gs // esp + + /* + * Now that the original eax/ebx are stored, we can use them safely + */ + + /* Store the address of the saved context */ + movl 60(%esp), %ebx + movl %esp, (%ebx) + + /* This is the proper context switch ! We change the stack here */ + movl 64(%esp), %esp + + /* Restore the CPU context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + addl $4, %esp /* Ignore "error code" */ + + /* This restores the eflags, the cs and the eip registers */ + iret /* equivalent to: popfl ; ret */ + +resume_pc: + // Same context as that when sos_cpu_context_switch got called + // arg2= to_context -- esp+8 + // arg1= from_context -- esp+4 + // caller ip -- esp + ret + + + +/* ------------------------- */ +.globl sos_cpu_context_exit_to +.type sos_cpu_context_exit_to, @function +sos_cpu_context_exit_to: + // arg3= reclaiming_arg -- esp+12 + // arg2= reclaiming_func -- esp+8 + // arg1= to_context -- esp+4 + // caller ip -- esp + + /* Store the current SP in a temporary register */ + movl %esp, %eax + + /* This is the proper context switch ! We change the stack here */ + movl 4(%eax), %esp + + /* Call the reclaiming function (remember: the old frame address + is stored in eax) */ + pushl 12(%eax) + call *8(%eax) + addl $4, %esp + + /* Restore the CPU context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + addl $4, %esp /* Ignore "error code" */ + + /* This restores the eflags, the cs and the eip registers */ + iret /* equivalent to: popfl ; ret */ diff --git a/sos-code-article6.5/hwcore/exception.c b/sos-code-article6.5/hwcore/exception.c new file mode 100644 index 0000000..ffad632 --- /dev/null +++ b/sos-code-article6.5/hwcore/exception.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "idt.h" +#include "irq.h" + +#include +#include "exception.h" + +/* array of exception wrappers, defined in exception_wrappers.S */ +extern sos_vaddr_t sos_exception_wrapper_array[SOS_EXCEPT_NUM]; + +/* arrays of exception handlers, shared with exception_wrappers.S */ +sos_exception_handler_t sos_exception_handler_array[SOS_EXCEPT_NUM] = + { NULL, }; + +/* List of exception names for the x86 architecture */ +static const char * sos_x86_exnames[] = { + [SOS_EXCEPT_DIVIDE_ERROR] = "Division by zero", + [SOS_EXCEPT_DEBUG] = "Debug", + [SOS_EXCEPT_NMI_INTERRUPT] = "Non Maskable Interrupt", + [SOS_EXCEPT_BREAKPOINT] = "Breakpoint", + [SOS_EXCEPT_OVERFLOW] = "Overflow", + [SOS_EXCEPT_BOUND_RANGE_EXCEDEED] = "Bound Range Exceeded", + [SOS_EXCEPT_INVALID_OPCODE] = "Invalid Opcode", + [SOS_EXCEPT_DEVICE_NOT_AVAILABLE] = "Device Unavailable", + [SOS_EXCEPT_DOUBLE_FAULT] = "Double Fault", + [SOS_EXCEPT_COPROCESSOR_SEGMENT_OVERRUN] = "Coprocessor Segment Overrun", + [SOS_EXCEPT_INVALID_TSS] = "Invalid TSS", + [SOS_EXCEPT_SEGMENT_NOT_PRESENT] = "Segment Not Present", + [SOS_EXCEPT_STACK_SEGMENT_FAULT] = "Stack Segfault", + [SOS_EXCEPT_GENERAL_PROTECTION] = "General Protection", + [SOS_EXCEPT_PAGE_FAULT] = "Page Fault", + [SOS_EXCEPT_INTEL_RESERVED_1] = "INTEL1", + [SOS_EXCEPT_FLOATING_POINT_ERROR] = "FP Error", + [SOS_EXCEPT_ALIGNEMENT_CHECK] = "Alignment Check", + [SOS_EXCEPT_MACHINE_CHECK] = "Machine Check", + [SOS_EXCEPT_INTEL_RESERVED_2] = "INTEL2", + [SOS_EXCEPT_INTEL_RESERVED_3] = "INTEL3", + [SOS_EXCEPT_INTEL_RESERVED_4] = "INTEL4", + [SOS_EXCEPT_INTEL_RESERVED_5] = "INTEL5", + [SOS_EXCEPT_INTEL_RESERVED_6] = "INTEL6", + [SOS_EXCEPT_INTEL_RESERVED_7] = "INTEL7", + [SOS_EXCEPT_INTEL_RESERVED_8] = "INTEL8", + [SOS_EXCEPT_INTEL_RESERVED_9] = "INTEL9", + [SOS_EXCEPT_INTEL_RESERVED_10] = "INTEL10", + [SOS_EXCEPT_INTEL_RESERVED_11] = "INTEL11", + [SOS_EXCEPT_INTEL_RESERVED_12] = "INTEL12", + [SOS_EXCEPT_INTEL_RESERVED_13] = "INTEL13", + [SOS_EXCEPT_INTEL_RESERVED_14] = "INTEL14" +}; + + +/* Catch-all exception handler */ +static void sos_generic_ex(int exid, const struct sos_cpu_state *ctxt) +{ + const char *exname = sos_exception_get_name(exid); + + sos_display_fatal_error("Exception %s in Kernel at instruction 0x%x (info=%x)!\n", + exname, + sos_cpu_context_get_PC(ctxt), + (unsigned)sos_cpu_context_get_EX_info(ctxt)); +} + + +sos_ret_t sos_exception_subsystem_setup(void) +{ + sos_ret_t retval; + int exid; + + /* Setup the generic exception handler by default for everybody + except for the double fault exception */ + for (exid = 0 ; exid < SOS_EXCEPT_NUM ; exid ++) + { + /* Skip double fault (see below) */ + if (exid == SOS_EXCEPT_DOUBLE_FAULT) + continue; + + retval = sos_exception_set_routine(exid, sos_generic_ex); + if (SOS_OK != retval) + return retval; + } + + + /* We inidicate that the double fault exception handler is defined, + and give its address. this handler is a do-nothing handler (see + exception_wrappers.S), and it can NOT be overriden by the + functions below */ + return sos_idt_set_handler(SOS_EXCEPT_BASE + SOS_EXCEPT_DOUBLE_FAULT, + (sos_vaddr_t) sos_exception_wrapper_array[SOS_EXCEPT_DOUBLE_FAULT], + 0 /* CPL0 routine */); +} + + +sos_ret_t sos_exception_set_routine(int exception_number, + sos_exception_handler_t routine) +{ + sos_ret_t retval; + sos_ui32_t flags; + + if ((exception_number < 0) || (exception_number >= SOS_EXCEPT_NUM)) + return -SOS_EINVAL; + + /* Double fault not supported */ + if (exception_number == SOS_EXCEPT_DOUBLE_FAULT) + return -SOS_ENOSUP; + + sos_disable_IRQs(flags); + + retval = SOS_OK; + + /* Set the exception routine to be called by the exception wrapper */ + sos_exception_handler_array[exception_number] = routine; + + /* If the exception is to be enabled, update the IDT with the exception + wrapper */ + if (routine != NULL) + retval + = sos_idt_set_handler(SOS_EXCEPT_BASE + exception_number, + (sos_vaddr_t) sos_exception_wrapper_array[exception_number], + 0 /* CPL0 routine */); + else /* Disable the IDT entry */ + retval + = sos_idt_set_handler(SOS_EXCEPT_BASE + exception_number, + (sos_vaddr_t)NULL /* No routine => disable IDTE */, + 0 /* don't care */); + + sos_restore_IRQs(flags); + return retval; +} + + +sos_exception_handler_t sos_exception_get_routine(int exception_number) +{ + if ((exception_number < 0) || (exception_number >= SOS_EXCEPT_NUM)) + return NULL; + + /* Double fault not supported */ + if (exception_number == SOS_EXCEPT_DOUBLE_FAULT) + return NULL; + + /* Expected to be atomic */ + return sos_exception_handler_array[exception_number]; +} + + +const char * sos_exception_get_name(int exception_number) +{ + if ((exception_number < 0) || (exception_number >= SOS_EXCEPT_NUM)) + return NULL; + + return sos_x86_exnames[exception_number]; +} diff --git a/sos-code-article6.5/hwcore/exception.h b/sos-code-article6.5/hwcore/exception.h new file mode 100644 index 0000000..c58bbb2 --- /dev/null +++ b/sos-code-article6.5/hwcore/exception.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_HWEXCEPT_H_ +#define _SOS_HWEXCEPT_H_ + +/** + * @file exception.c + * + * Hardware exception routines management. + */ + +#ifndef ASM_SOURCE +# include +# include "cpu_context.h" +#endif + +/** + * Standard Intel x86 exceptions. + * + * @see Intel x86 doc vol 3, section 5.12. + */ +#define SOS_EXCEPT_DIVIDE_ERROR 0 // No error code +#define SOS_EXCEPT_DEBUG 1 // No error code +#define SOS_EXCEPT_NMI_INTERRUPT 2 // No error code +#define SOS_EXCEPT_BREAKPOINT 3 // No error code +#define SOS_EXCEPT_OVERFLOW 4 // No error code +#define SOS_EXCEPT_BOUND_RANGE_EXCEDEED 5 // No error code +#define SOS_EXCEPT_INVALID_OPCODE 6 // No error code +#define SOS_EXCEPT_DEVICE_NOT_AVAILABLE 7 // No error code +#define SOS_EXCEPT_DOUBLE_FAULT 8 // Yes (Zero) +#define SOS_EXCEPT_COPROCESSOR_SEGMENT_OVERRUN 9 // No error code +#define SOS_EXCEPT_INVALID_TSS 10 // Yes +#define SOS_EXCEPT_SEGMENT_NOT_PRESENT 11 // Yes +#define SOS_EXCEPT_STACK_SEGMENT_FAULT 12 // Yes +#define SOS_EXCEPT_GENERAL_PROTECTION 13 // Yes +#define SOS_EXCEPT_PAGE_FAULT 14 // Yes +#define SOS_EXCEPT_INTEL_RESERVED_1 15 // No +#define SOS_EXCEPT_FLOATING_POINT_ERROR 16 // No +#define SOS_EXCEPT_ALIGNEMENT_CHECK 17 // Yes (Zero) +#define SOS_EXCEPT_MACHINE_CHECK 18 // No +#define SOS_EXCEPT_INTEL_RESERVED_2 19 // No +#define SOS_EXCEPT_INTEL_RESERVED_3 20 // No +#define SOS_EXCEPT_INTEL_RESERVED_4 21 // No +#define SOS_EXCEPT_INTEL_RESERVED_5 22 // No +#define SOS_EXCEPT_INTEL_RESERVED_6 23 // No +#define SOS_EXCEPT_INTEL_RESERVED_7 24 // No +#define SOS_EXCEPT_INTEL_RESERVED_8 25 // No +#define SOS_EXCEPT_INTEL_RESERVED_9 26 // No +#define SOS_EXCEPT_INTEL_RESERVED_10 27 // No +#define SOS_EXCEPT_INTEL_RESERVED_11 28 // No +#define SOS_EXCEPT_INTEL_RESERVED_12 29 // No +#define SOS_EXCEPT_INTEL_RESERVED_13 30 // No +#define SOS_EXCEPT_INTEL_RESERVED_14 31 // No + +#ifndef ASM_SOURCE + +typedef void (*sos_exception_handler_t)(int exception_number, + const struct sos_cpu_state *cpu_kstate); + +sos_ret_t sos_exception_subsystem_setup(void); +sos_ret_t sos_exception_set_routine(int exception_number, + sos_exception_handler_t routine); +sos_exception_handler_t sos_exception_get_routine(int exception_number); + +const char * sos_exception_get_name(int exception_number); +#endif /* ! ASM_SOURCE */ + +#endif /* _SOS_HWEXCEPT_H_ */ diff --git a/sos-code-article6.5/hwcore/exception_wrappers.S b/sos-code-article6.5/hwcore/exception_wrappers.S new file mode 100644 index 0000000..369614a --- /dev/null +++ b/sos-code-article6.5/hwcore/exception_wrappers.S @@ -0,0 +1,221 @@ +/* Copyright (C) 2004 The KOS Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "exception.h" + +.file "exception_wrappers.S" + +.text + +/* The address of the table of handlers (defined in exception.c) */ +.extern sos_exception_handler_array + +/* The address of the table of wrappers (defined below, and shared + with exception.c */ +.globl sos_exception_wrapper_array + +/** + * For exceptions with/without error code, refer to Intel x86 doc vol 3, + * section 5.12 + */ + +/* These wrappers are for exceptions without error code */ +.irp id, \ + SOS_EXCEPT_DIVIDE_ERROR, \ + SOS_EXCEPT_DEBUG, \ + SOS_EXCEPT_NMI_INTERRUPT, \ + SOS_EXCEPT_BREAKPOINT, \ + SOS_EXCEPT_OVERFLOW, \ + SOS_EXCEPT_BOUND_RANGE_EXCEDEED, \ + SOS_EXCEPT_INVALID_OPCODE, \ + SOS_EXCEPT_DEVICE_NOT_AVAILABLE, \ + SOS_EXCEPT_COPROCESSOR_SEGMENT_OVERRUN, \ + SOS_EXCEPT_INTEL_RESERVED_1, \ + SOS_EXCEPT_FLOATING_POINT_ERROR, \ + SOS_EXCEPT_MACHINE_CHECK, \ + SOS_EXCEPT_INTEL_RESERVED_2, \ + SOS_EXCEPT_INTEL_RESERVED_3, \ + SOS_EXCEPT_INTEL_RESERVED_4, \ + SOS_EXCEPT_INTEL_RESERVED_5, \ + SOS_EXCEPT_INTEL_RESERVED_6, \ + SOS_EXCEPT_INTEL_RESERVED_7, \ + SOS_EXCEPT_INTEL_RESERVED_8, \ + SOS_EXCEPT_INTEL_RESERVED_9, \ + SOS_EXCEPT_INTEL_RESERVED_10, \ + SOS_EXCEPT_INTEL_RESERVED_11, \ + SOS_EXCEPT_INTEL_RESERVED_12, \ + SOS_EXCEPT_INTEL_RESERVED_13, \ + SOS_EXCEPT_INTEL_RESERVED_14 + + .p2align 2, 0x90 + sos_exception_wrapper_\id: + .type sos_exception_wrapper_\id,@function + + /* Fake error code */ + pushl $0 + /* Backup the context */ + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* + * Call the handler with the exception number and the + * address of the stored CPU context as arguments + */ + pushl %esp + pushl $\id + leal sos_exception_handler_array,%edi + call *\id*4(%edi) + /* Unallocate the arguments passed to the handler */ + addl $8, %esp + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + + popl %ebp + /* Remove fake error code */ + addl $4, %esp + iret +.endr + + /* These wrappers are for exceptions with error code */ +.irp id, \ + SOS_EXCEPT_INVALID_TSS, \ + SOS_EXCEPT_SEGMENT_NOT_PRESENT, \ + SOS_EXCEPT_STACK_SEGMENT_FAULT, \ + SOS_EXCEPT_GENERAL_PROTECTION, \ + SOS_EXCEPT_PAGE_FAULT, \ + SOS_EXCEPT_ALIGNEMENT_CHECK + + .p2align 2, 0x90 + sos_exception_wrapper_\id: + .type sos_exception_wrapper_\id,@function + + /* ret eflags */ + /* ret cs */ + /* ret eip */ + /* Error code */ + + /* Backup the context */ + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* + * Call the handler with the exception number and the + * address of the stored CPU context as arguments + */ + pushl %esp + pushl $\id + leal sos_exception_handler_array,%edi + call *\id*4(%edi) + /* Unallocate the arguments passed to the handler */ + addl $8, %esp + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + + /* Error code isn't compatible with iretd */ + addl $4, %esp + + iret +.endr + + +/* Double fault handler not supported. We must define it since we + define an entry for it in the sos_exception_wrapper_array. It + simply uses an alternate stack to display a message and stop the + system. qemu won't handle it correctly (see comment in qemu's + sources). */ +#define ALTERNATE_DOUBLE_FAULT_STACK_SIZE 512 +.irp id, SOS_EXCEPT_DOUBLE_FAULT +.p2align 2, 0x90 +sos_exception_wrapper_\id: +.type sos_exception_wrapper_\id,@function +1: cli /* Not necessary */ + movl $double_fault_alternate_stack, %eax + addl $ALTERNATE_DOUBLE_FAULT_STACK_SIZE, %eax + movl %eax, %esp + pushl $msg_double_fault_not_supported + call sos_display_fatal_error ; jmp 1b /* Not necessary */ +.endr + +.section ".rodata" +msg_double_fault_not_supported: + .string "exception_wrappers.S: Double fault detected ! NOT SUPPORTED yet. System Halted." + +/* Build the sos_irq_wrapper_array, shared with interrupt.c */ +.p2align 5, 0x0 +sos_exception_wrapper_array: + .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \ + 16,17,18,19,20,21,22,23,24,25,26,27,29,30,31 + .long (sos_exception_wrapper_\id) + .endr + +/* Alternate stack for double fault handler */ +.bss +.p2align 2, 0x0 +.size double_fault_alternate_stack, ALTERNATE_DOUBLE_FAULT_STACK_SIZE +double_fault_alternate_stack: + .fill ALTERNATE_DOUBLE_FAULT_STACK_SIZE, 1, 0x0 diff --git a/sos-code-article6.5/hwcore/gdt.c b/sos-code-article6.5/hwcore/gdt.c new file mode 100644 index 0000000..781c9ca --- /dev/null +++ b/sos-code-article6.5/hwcore/gdt.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2004 David Decotigny + Copyright (C) 2003 Thomas Petazzoni + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "segment.h" + +#include "gdt.h" + + +/** + * The sructure of a segment descriptor. + * + * @see Intel x86 doc, Vol 3, section 3.4.3, figure 3-8. For segment + * types, see section 3.5 + */ +struct x86_segment_descriptor +{ + /* Lowest dword */ + sos_ui16_t limit_15_0; /* Segment limit, bits 15..0 */ + sos_ui16_t base_paged_addr_15_0; /* Base address, bits 15..0 */ + + /* Highest dword */ + sos_ui8_t base_paged_addr_23_16; /* Base address bits 23..16 */ + sos_ui8_t segment_type:4; /* Section 3.4.3.1 (code/data) + and 3.5 (system) of Intel x86 vol 3 */ + sos_ui8_t descriptor_type:1; /* 0=system, 1=Code/Data */ + sos_ui8_t dpl:2; /* Descriptor privilege level */ + sos_ui8_t present:1; + + sos_ui8_t limit_19_16:4; /* Segment limit, bits 19..16 */ + sos_ui8_t custom:1; + sos_ui8_t zero:1; + sos_ui8_t op_size:1; /* 0=16bits instructions, 1=32bits */ + sos_ui8_t granularity:1; /* 0=limit in bytes, 1=limit in pages */ + + sos_ui8_t base_paged_addr_31_24; /* Base address bits 31..24 */ +} __attribute__ ((packed, aligned (8))); + + +/** + * The GDT register, which stores the address and size of the + * GDT. + * + * @see Intel x86 doc vol 3, section 2.4, figure 2-4; and section + * 3.5.1 + */ +struct x86_gdt_register { + /* The maximum GDT offset allowed to access an entry in the GDT */ + sos_ui16_t limit; + + /* This is not exactly a "virtual" address, ie an adddress such as + those of instructions and data; this is a "linear" address, ie an + address in the paged memory. However, in SOS we configure the + segmented memory as a "flat" space: the 0-4GB segment-based (ie + "virtual") addresses directly map to the 0-4GB paged memory (ie + "linear"), so that the "linear" addresses are numerically equal + to the "virtual" addresses: this base_addr will thus be the same + as the address of the gdt array */ + sos_ui32_t base_addr; +} __attribute__((packed, aligned(8))); + + +/** + * Helper macro that builds a Segment descriptor for the virtual + * 0..4GB addresses to be mapped to the linear 0..4GB linear + * addresses. + */ +#define BUILD_GDTE(descr_privilege_level,is_code) \ + ((struct x86_segment_descriptor) { \ + .limit_15_0= 0xffff, \ + .base_paged_addr_15_0= 0, \ + .base_paged_addr_23_16= 0, \ + .segment_type= ((is_code)?0xb:0x3), \ + /* With descriptor_type (below) = 1 (code/data), \ + * see Figure 3-1 of section 3.4.3.1 in Intel \ + * x86 vol 3: \ + * - Code (bit 3 = 1): \ + * bit 0: 1=Accessed \ + * bit 1: 1=Readable \ + * bit 2: 0=Non-Conforming \ + * - Data (bit 3 = 0): \ + * bit 0: 1=Accessed \ + * bit 1: 1=Writable \ + * bit 2: 0=Expand up (stack-related) \ + * For Conforming/non conforming segments, see \ + * Intel x86 Vol 3 section 4.8.1.1 \ + */ \ + .descriptor_type= 1, /* 1=Code/Data */ \ + .dpl= ((descr_privilege_level) & 0x3), \ + .present= 1, \ + .limit_19_16= 0xf, \ + .custom= 0, \ + .op_size= 1, /* 32 bits instr/data */ \ + .granularity= 1 /* limit is in 4kB Pages */ \ + }) + + +/** The actual GDT */ +static struct x86_segment_descriptor gdt[] = { + [SOS_SEG_NULL] = (struct x86_segment_descriptor){ 0, }, + [SOS_SEG_KCODE] = BUILD_GDTE(0, 1), + [SOS_SEG_KDATA] = BUILD_GDTE(0, 0), +}; + +sos_ret_t sos_gdt_subsystem_setup(void) +{ + struct x86_gdt_register gdtr; + + /* Address of the GDT */ + gdtr.base_addr = (sos_ui32_t) gdt; + + /* The limit is the maximum offset in bytes from the base address of + the GDT */ + gdtr.limit = sizeof(gdt) - 1; + + /* Commit the GDT into the CPU, and update the segment + registers. The CS register may only be updated with a long jump + to an absolute address in the given segment (see Intel x86 doc + vol 3, section 4.8.1). */ + asm volatile ("lgdt %0 \n\ + ljmp %1,$1f \n\ + 1: \n\ + movw %2, %%ax \n\ + movw %%ax, %%ss \n\ + movw %%ax, %%ds \n\ + movw %%ax, %%es \n\ + movw %%ax, %%fs \n\ + movw %%ax, %%gs" + : + :"m"(gdtr), + "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE)), + "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA)) + :"memory","eax"); + + return SOS_OK; +} + + diff --git a/sos-code-article6.5/hwcore/gdt.h b/sos-code-article6.5/hwcore/gdt.h new file mode 100644 index 0000000..323b784 --- /dev/null +++ b/sos-code-article6.5/hwcore/gdt.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_GDT_H_ +#define _SOS_GDT_H_ + +/** + * @file gdt.h + * + * The routines that manage the GDT, the table that maps the virtual + * addresses (data/instructions, segment-relative), to "linear" + * addresses (ie paged-memory). In SOS/x86, we use a "flat" virtual + * space, ie the virtual and linear spaces are equivalent. + * + * @see Intel x86 doc vol 3, chapter 3 + */ + +#include +#include + +/** + * Configure the virtual space as a direct mapping to the linear + * address space (ie "flat" virtual space). + */ +sos_ret_t sos_gdt_subsystem_setup(void); + + +#endif /* _SOS_GDT_H_ */ diff --git a/sos-code-article6.5/hwcore/i8254.c b/sos-code-article6.5/hwcore/i8254.c new file mode 100644 index 0000000..5198129 --- /dev/null +++ b/sos-code-article6.5/hwcore/i8254.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2004 The KOS Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include + +#include "i8254.h" + +/** 82c54 clock frequency */ +#define I8254_MAX_FREQ 1193180 + +/* Ports to communicate with the 82c54 */ +#define I8254_TIMER0 0x40 +#define I8254_TIMER1 0x41 +#define I8254_TIMER2 0x42 +#define I8254_CONTROL 0x43 + +/** + * Configure the first timer of the 82c54 chip as a rate generator, + * which will raise an IRQ0 on a regular periodic basis, as given by + * the freq parameter. Second (RAM refresh) and third (speaker) timers + * are left unchanged. Maximum frequency is that of the 8254 clock, ie + * 1193180 Hz. + * + * Ahhh PC systems are nice toys: this maximum "strange" frequency + * equals that of the NTSC clock (14.31818 MHz) divided by 12. In + * turn, the famous 4.77 MHz cpu clock frequency of the first IBM PC + * is this same NTSC frequency divided by 3. Why the NTSC frequency as + * a base "standard" ? Because the 14.31818 MHz quartz were cheap at + * that time, and because it allows to simply drive altogether the + * cpu, the "time of day" timer, and the video signal generators. + */ +sos_ret_t sos_i8254_set_frequency(unsigned int freq) +{ + unsigned int nb_tick; + + if (freq <= 0) + return -SOS_EINVAL; + + /* Compute counter value */ + nb_tick = I8254_MAX_FREQ / freq; + + /* Counter must be between 1 and 65536 */ + if (nb_tick > 65536) + return -SOS_EINVAL; + if (nb_tick <= 0) + return -SOS_EINVAL; + + /* The i8254 interprets 0 to mean counter == 65536, because 65536 + cannot be coded on 16bits */ + if (nb_tick == 65536) + nb_tick = 0; + + /* We want to configure timer0, we want to send both LSB+MSB to set + timer0 freq (-> 0x30), and we configure timer0 in mode 2, ie as a + rate generator (-> 0x4) ==> 0x34 */ + outb(0x34, I8254_CONTROL); + + /* Send LSB of counter first */ + outb((nb_tick & 0xFF), I8254_TIMER0); + + /* Send MSB of counter */ + outb((nb_tick >> 8) & 0xFF, I8254_TIMER0); + + return SOS_OK; +} diff --git a/sos-code-article6.5/hwcore/i8254.h b/sos-code-article6.5/hwcore/i8254.h new file mode 100644 index 0000000..406b713 --- /dev/null +++ b/sos-code-article6.5/hwcore/i8254.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_i8259_H_ +#define _SOS_i8259_H_ + +#include + +/** + * @file i8254.h PC programmable timer + * + * Programmable timer routines. See the Intel 82C54 datasheet (on kos + * website). + * + * @see i82C54 datasheet on Kos website. + */ + +/** Change timer interrupt (IRQ 0) frequency */ +sos_ret_t sos_i8254_set_frequency(unsigned int freq); + +#endif /* _SOS_i8259_H_ */ diff --git a/sos-code-article6.5/hwcore/i8259.c b/sos-code-article6.5/hwcore/i8259.c new file mode 100644 index 0000000..d6c6c60 --- /dev/null +++ b/sos-code-article6.5/hwcore/i8259.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2004 The KOS Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "ioports.h" + +#include "i8259.h" + +#define PIC_MASTER 0x20 +#define PIC_SLAVE 0xa0 + +/** Setup the 8259 PIC */ +sos_ret_t sos_i8259_subsystem_setup(void) +{ + /* Send ICW1: 8086 mode + NOT Single ctrl + call address + interval=8 */ + outb(0x11, PIC_MASTER); + outb(0x11, PIC_SLAVE); + + /* Send ICW2: ctrl base address */ + outb(0x20, PIC_MASTER+1); + outb(0x28, PIC_SLAVE+1); + + /* Send ICW3 master: mask where slaves are connected */ + outb(0x4, PIC_MASTER+1); + /* Send ICW3 slave: index where the slave is connected on master */ + outb(0x2, PIC_SLAVE+1); + + /* Send ICW4: 8086 mode, fully nested, not buffered, no implicit EOI */ + outb(0x1, PIC_MASTER+1); + outb(0x1, PIC_SLAVE+1); + + /* Send OCW1: + * Closing all IRQs : waiting for a correct handler The only IRQ + * enabled is the cascade (that's why we use 0xFB for the master) */ + outb(0xFB, PIC_MASTER+1); + outb(0xFF, PIC_SLAVE+1); + + return SOS_OK; +} + + +sos_ret_t sos_i8259_enable_irq_line(int numirq) +{ + if(numirq < 8) + /* irq on master PIC */ + outb((inb(PIC_MASTER+1) & ~(1 << numirq)), PIC_MASTER+1); + else + /* irq on slave PIC */ + outb((inb(PIC_SLAVE+1) & ~(1 << (numirq-8))), PIC_SLAVE+1); + + return SOS_OK; +} + + +sos_ret_t sos_i8259_disable_irq_line(int numirq) +{ + if(numirq < 8) + /* irq on master PIC */ + outb((inb(PIC_MASTER+1) | (1 << numirq)), PIC_MASTER+1); + else + /* irq on slave PIC */ + outb((inb(PIC_SLAVE+1) | (1 << (numirq-8))), PIC_SLAVE+1); + + return SOS_OK; +} diff --git a/sos-code-article6.5/hwcore/i8259.h b/sos-code-article6.5/hwcore/i8259.h new file mode 100644 index 0000000..0820524 --- /dev/null +++ b/sos-code-article6.5/hwcore/i8259.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_i8259_H_ +#define _SOS_i8259_H_ + +#include + +/** + * @file i8259.h PIC + * + * PIC Management routines. See the Intel 8259A datasheet (on kos + * website), page 9+. Should be not be used directly: only interrupt.c + * should use this. + * + * @see i8259A datasheet on Kos website. + */ + +/** Setup PIC and Disable all IRQ lines */ +sos_ret_t sos_i8259_subsystem_setup(void); + +sos_ret_t sos_i8259_enable_irq_line(int numirq); + +sos_ret_t sos_i8259_disable_irq_line(int numirq); + +#endif /* _SOS_i8259_H_ */ diff --git a/sos-code-article6.5/hwcore/idt.c b/sos-code-article6.5/hwcore/idt.c new file mode 100644 index 0000000..07db4ab --- /dev/null +++ b/sos-code-article6.5/hwcore/idt.c @@ -0,0 +1,159 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "segment.h" + +#include "idt.h" + +/** + * An entry in the IDT, or "IDTE" in the following, ie a reference to + * a interrupt/trap routine or a task gate to handle the sw/hw + * interrupts and exceptions. + * + * @see figure 5-2, intel x86 doc, vol 3 + */ +struct x86_idt_entry +{ + /* Low dword */ + sos_ui16_t offset_low; /* 15..0, offset of the routine in the segment */ + sos_ui16_t seg_sel; /* 31..16, the ID of the segment */ + + /* High dword */ + sos_ui8_t reserved:5; /* 4..0 */ + sos_ui8_t flags:3; /* 7..5 */ + sos_ui8_t type:3; /* 10..8 (interrupt gate, trap gate...) */ + sos_ui8_t op_size:1; /* 11 (0=16bits instructions, 1=32bits instr.) */ + sos_ui8_t zero:1; /* 12 */ + sos_ui8_t dpl:2; /* 14..13 */ + sos_ui8_t present:1; /* 15 */ + sos_ui16_t offset_high; /* 31..16 */ +} __attribute__((packed)); + + +/** + * The IDT register, which stores the address and size of the + * IDT. + * + * @see Intel x86 doc vol 3, section 2.4, figure 2-4 + */ +struct x86_idt_register +{ + /* The maximum GDT offset allowed to access an entry in the GDT */ + sos_ui16_t limit; + + /* This is not exactly a "virtual" address, ie an adddress such as + those of instructions and data; this is a "linear" address, ie an + address in the paged memory. However, in SOS we configure the + segmented memory as a "flat" space: the 0-4GB segment-based (ie + "virtual") addresses directly map to the 0-4GB paged memory (ie + "linear"), so that the "linear" addresses are numerically equal + to the "virtual" addresses: this base_addr will thus be the same + as the address of the gdt array */ + sos_ui32_t base_addr; +} __attribute__((packed, aligned (8))); + + +static struct x86_idt_entry idt[SOS_IDTE_NUM]; + +sos_ret_t sos_idt_subsystem_setup() +{ + struct x86_idt_register idtr; + int i; + + for (i = 0 ; + i < SOS_IDTE_NUM ; + i++) + { + struct x86_idt_entry *idte = idt + i; + + /* Setup an empty IDTE interrupt gate, see figure 5-2 in Intel + x86 doc, vol 3 */ + idte->seg_sel = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE); + idte->reserved = 0; + idte->flags = 0; + idte->type = 0x6; /* Interrupt gate (110b) */ + idte->op_size = 1; /* 32bits instructions */ + idte->zero = 0; + + /* Disable this IDT entry for the moment */ + sos_idt_set_handler(i, (sos_vaddr_t)NULL, 0/* Don't care */); + } + + /* + * Setup the IDT register, see Intel x86 doc vol 3, section 5.8. + */ + + /* Address of the IDT */ + idtr.base_addr = (sos_ui32_t) idt; + + /* The limit is the maximum offset in bytes from the base address of + the IDT */ + idtr.limit = sizeof(idt) - 1; + + /* Commit the IDT into the CPU */ + asm volatile ("lidt %0\n"::"m"(idtr):"memory"); + + return SOS_OK; +} + + +sos_ret_t sos_idt_set_handler(int index, + sos_vaddr_t handler_address, + int lowest_priviledge /* 0..3 */) +{ + struct x86_idt_entry *idte; + + if ((index < 0) || (index >= SOS_IDTE_NUM)) + return -SOS_EINVAL; + if ((lowest_priviledge < 0) || (lowest_priviledge > 3)) + return -SOS_EINVAL; + + idte = idt + index; + if (handler_address != (sos_vaddr_t)NULL) + { + idte->offset_low = handler_address & 0xffff; + idte->offset_high = (handler_address >> 16) & 0xffff; + idte->dpl = lowest_priviledge; + idte->present = 1; /* Yes, there is a handler */ + } + else /* Disable this IDT entry */ + { + idte->offset_low = 0; + idte->offset_high = 0; + idte->dpl = 0; + idte->present = 0; /* No, there is no handler */ + } + + return SOS_OK; +} + + +sos_ret_t sos_idt_get_handler(int index, + sos_vaddr_t *handler_address, + int *lowest_priviledge) +{ + if ((index < 0) || (index >= SOS_IDTE_NUM)) + return -SOS_EINVAL; + + if (handler_address != NULL) + *handler_address = idt[index].offset_low + | (idt[index].offset_high << 16); + if (lowest_priviledge != NULL) + *lowest_priviledge = idt[index].dpl; + + return SOS_OK; +} diff --git a/sos-code-article6.5/hwcore/idt.h b/sos-code-article6.5/hwcore/idt.h new file mode 100644 index 0000000..2d165b0 --- /dev/null +++ b/sos-code-article6.5/hwcore/idt.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_IDT_H_ +#define _SOS_IDT_H_ + +/** + * @file idt.h + * + * Manage the x86 Interrupt Descriptor Table, the table which maps the + * hardware interrupt lines, hardware exceptions, and software + * interrupts, to software routines. We only define "interrupt gate" + * IDT entries. Don't use it directly; refer instead to interrupt.c, + * exceptions.c and syscall.c. + * + * @see Intel x86 doc, Vol 3, chapter 5 + */ + +#include +#include + +/* Mapping of the CPU exceptions in the IDT (imposed by Intel + standards) */ +#define SOS_EXCEPT_BASE 0 +#define SOS_EXCEPT_NUM 32 +#define SOS_EXCEPT_MAX (SOS_HWEXCEPT_BASE + SOS_HWEXCEPT_NUM - 1) + +/* Mapping of the IRQ lines in the IDT */ +#define SOS_IRQ_BASE 32 +#define SOS_IRQ_NUM 16 +#define SOS_IRQ_MAX (SOS_IRQ_BASE + SOS_IRQ_NUM - 1) + +/** + * Number of IDT entries. + * + * @note Must be large enough to map the hw interrupts, the exceptions + * (=> total is 48 entries), and the syscall(s). Since our syscall + * will be 0x42, it must be >= 0x43. Intel doc limits this to 256 + * entries, we use this limit. + */ +#define SOS_IDTE_NUM 256 /* 0x100 */ + +/** Initialization routine: all the IDT entries (or "IDTE") are marked + "not present". */ +sos_ret_t sos_idt_subsystem_setup(void); + +/** + * Enable the IDT entry if handler_address != NULL, with the given + * lowest_priviledge.\ Disable the IDT entry when handler_address == + * NULL (the lowest_priviledge parameter is then ignored). Intel doc + * says that there must not be more than 256 entries. + * + * @note IRQ Unsafe + */ +sos_ret_t sos_idt_set_handler(int index, + sos_vaddr_t handler_address, + int lowest_priviledge /* 0..3 */); + + +/** + * @note IRQ Unsafe + * + * @return the handler address and DPL in the 2nd and 3rd + * parameters + */ +sos_ret_t sos_idt_get_handler(int index, + sos_vaddr_t *handler_address, + int *lowest_priviledge); + +#endif /* _SOS_IDT_H_ */ diff --git a/sos-code-article6.5/hwcore/ioports.h b/sos-code-article6.5/hwcore/ioports.h new file mode 100644 index 0000000..69c6d15 --- /dev/null +++ b/sos-code-article6.5/hwcore/ioports.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2004 All GPL'ed OS + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_IOPORTS_H_ +#define _SOS_IOPORTS_H_ + +/** + * @ioports.h + * + * Intel-specific I/O space access routines. + */ + +/* This macro allows to write to an I/O port */ +#define outb(value, port) \ + __asm__ volatile ( \ + "outb %b0,%w1" \ + ::"a" (value),"Nd" (port) \ + ) \ + +// read one byte from port +#define inb(port) \ +({ \ + unsigned char _v; \ + __asm__ volatile ( \ + "inb %w1,%0" \ + :"=a" (_v) \ + :"Nd" (port) \ + ); \ + _v; \ +}) + +#endif /* _SOS_IOPORTS_H_ */ diff --git a/sos-code-article6.5/hwcore/irq.c b/sos-code-article6.5/hwcore/irq.c new file mode 100644 index 0000000..6722027 --- /dev/null +++ b/sos-code-article6.5/hwcore/irq.c @@ -0,0 +1,100 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "idt.h" +#include "i8259.h" + +#include "irq.h" + +/** array of IRQ wrappers, defined in irq_wrappers.S */ +extern sos_vaddr_t sos_irq_wrapper_array[SOS_IRQ_NUM]; + +/** arrays of IRQ handlers, shared with irq_wrappers.S */ +sos_irq_handler_t sos_irq_handler_array[SOS_IRQ_NUM] = { NULL, }; + +/** Number of interrupt handlers that are currently executing */ +sos_ui32_t sos_irq_nested_level_counter; + +sos_ret_t sos_irq_subsystem_setup(void) +{ + sos_irq_nested_level_counter = 0; + return sos_i8259_subsystem_setup(); +} + + +sos_ret_t sos_irq_set_routine(int irq_level, + sos_irq_handler_t routine) +{ + sos_ret_t retval; + sos_ui32_t flags; + + if ((irq_level < 0) || (irq_level >= SOS_IRQ_NUM)) + return -SOS_EINVAL; + + sos_disable_IRQs(flags); + + retval = SOS_OK; + + /* Set the irq routine to be called by the IRQ wrapper */ + sos_irq_handler_array[irq_level] = routine; + + /* If the irq is to be enabled, update the IDT with the IRQ + wrapper */ + if (routine != NULL) + { + retval + = sos_idt_set_handler(SOS_IRQ_BASE + irq_level, + (sos_vaddr_t) sos_irq_wrapper_array[irq_level], + 0 /* CPL0 routine */); + /* A problem occured */ + if (retval != SOS_OK) + sos_irq_handler_array[irq_level] = NULL; + } + else /* Disable this idt entry */ + { + retval + = sos_idt_set_handler(SOS_IRQ_BASE + irq_level, + (sos_vaddr_t)NULL /* Disable IDTE */, + 0 /* Don't care */); + } + + /* Update the PIC only if an IRQ handler has been set */ + if (sos_irq_handler_array[irq_level] != NULL) + sos_i8259_enable_irq_line(irq_level); + else + sos_i8259_disable_irq_line(irq_level); + + sos_restore_IRQs(flags); + return retval; +} + + +sos_irq_handler_t sos_irq_get_routine(int irq_level) +{ + if ((irq_level < 0) || (irq_level >= SOS_IRQ_NUM)) + return NULL; + + /* Expected to be atomic */ + return sos_irq_handler_array[irq_level]; +} + + +sos_ui32_t sos_irq_get_nested_level() +{ + /* No need to disable interrupts here */ + return sos_irq_nested_level_counter; +} diff --git a/sos-code-article6.5/hwcore/irq.h b/sos-code-article6.5/hwcore/irq.h new file mode 100644 index 0000000..c067cc4 --- /dev/null +++ b/sos-code-article6.5/hwcore/irq.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_HWINTR_H_ +#define _SOS_HWINTR_H_ + + +/** + * @file irq.c + * + * Hardware interrupts routines management. + */ + + +#include +#include "cpu_context.h" + + +#define sos_save_flags(flags) \ + asm volatile("pushfl ; popl %0":"=g"(flags)::"memory") +#define sos_restore_flags(flags) \ + asm volatile("push %0; popfl"::"g"(flags):"memory") + + +#define sos_disable_IRQs(flags) \ + ({ sos_save_flags(flags); asm("cli\n"); }) +#define sos_restore_IRQs(flags) \ + sos_restore_flags(flags) + + +/* Usual IRQ levels */ +#define SOS_IRQ_TIMER 0 +#define SOS_IRQ_KEYBOARD 1 +#define SOS_IRQ_SLAVE_PIC 2 +#define SOS_IRQ_COM2 3 +#define SOS_IRQ_COM1 4 +#define SOS_IRQ_LPT2 5 +#define SOS_IRQ_FLOPPY 6 +#define SOS_IRQ_LPT1 7 +#define SOS_IRQ_8_NOT_DEFINED 8 +#define SOS_IRQ_RESERVED_1 9 +#define SOS_IRQ_RESERVED_2 10 +#define SOS_IRQ_RESERVED_3 11 +#define SOS_IRQ_RESERVED_4 12 +#define SOS_IRQ_COPROCESSOR 13 +#define SOS_IRQ_HARDDISK 14 +#define SOS_IRQ_RESERVED_5 15 + + +/** Definition of an hardware IRQ handler */ +typedef void (*sos_irq_handler_t)(int irq_level); + + +/** Setup the PIC */ +sos_ret_t sos_irq_subsystem_setup(void); + + +/** + * If the routine is not NULL, the IDT is setup to call an IRQ + * wrapper upon interrupt, which in turn will call the routine, and + * the PIC is programmed to raise an irq.\ If the routine is + * NULL, we disable the irq line. + */ +sos_ret_t sos_irq_set_routine(int irq_level, + sos_irq_handler_t routine); + +sos_irq_handler_t sos_irq_get_routine(int irq_level); + + +/** + * Tell how many nested IRQ handler have been fired + */ +sos_ui32_t sos_irq_get_nested_level(); + + +/** + * Return TRUE when we are currently executing in interrupt context + */ +#define sos_servicing_irq() \ + (sos_irq_get_nested_level() > 0) + + +#endif /* _SOS_HWINTR_H_ */ diff --git a/sos-code-article6.5/hwcore/irq_wrappers.S b/sos-code-article6.5/hwcore/irq_wrappers.S new file mode 100644 index 0000000..8945123 --- /dev/null +++ b/sos-code-article6.5/hwcore/irq_wrappers.S @@ -0,0 +1,220 @@ +/* Copyright (C) 2004 The KOS Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ + +.file "irq_wrappers.S" + +.text + +/** The address of the table of handlers (defined in irq.c) */ +.extern sos_irq_handler_array + +/** The address of the table of wrappers (defined below, and shared + with irq.c */ +.globl sos_irq_wrapper_array + +/** The variable holding the nested level of the IRQ handlers */ +.extern sos_irq_nested_level_counter + +/* These pre-handlers are for IRQ (Master PIC) */ +.irp id, 0,1,2,3,4,5,6,7 + + .p2align 2, 0x90 + + sos_irq_wrapper_\id: + .type sos_irq_wrapper_\id,@function + + /* + * Backup the CPU context + */ + + /* Fake error code */ + pushl $0 + + /* Backup the actual context */ + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* + * Increment IRQ nested level + */ + incl sos_irq_nested_level_counter + + /* Send EOI to PIC. See Intel 8259 datasheet + available on Kos website */ + movb $0x20, %al + outb %al, $0x20 + + /* + * Call the handler with IRQ number as argument + */ + pushl $\id + leal sos_irq_handler_array,%edi + call *\id*4(%edi) + addl $4, %esp + + /* + * Decrement IRQ nested level + */ + cli /* Just in case we messed up everything in the handler */ + subl $1, sos_irq_nested_level_counter + + /* sos_irq_nested_level_counter went below 0 ?! */ + jnc 2f + + 1: /* Yes: Print fatal error message */ + pushl $msg_nested_level_overflow + call sos_display_fatal_error + addl $4, %esp ; jmp 1b + /* Never returns */ + + 2: /* No: all right ! */ + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + + /* Remove fake error code */ + addl $4, %esp + + iret + .endr + + +/* These pre-handlers are for IRQ (Slave PIC) */ +.irp id, 8,9,10,11,12,13,14,15 + + .p2align 2, 0x90 + + sos_irq_wrapper_\id: + .type sos_irq_wrapper_\id,@function + + /* + * Backup the CPU context + */ + + /* Fake error code */ + pushl $0 + + /* Backup the actual context */ + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* + * Increment IRQ nested level + */ + incl sos_irq_nested_level_counter + + /* Send EOI to PIC. See Intel 8259 datasheet + available on Kos website */ + movb $0x20, %al + outb %al, $0xa0 + outb %al, $0x20 + + /* + * Call the handler with IRQ number as argument + */ + pushl $\id + leal sos_irq_handler_array,%edi + call *\id*4(%edi) + addl $4, %esp + + /* + * Decrement IRQ nested level + */ + cli /* Just in case we messed up everything in the handler */ + subl $1, sos_irq_nested_level_counter + + /* sos_irq_nested_level_counter went below 0 ?! */ + jnc 2f + + 1: /* Yes: Print fatal error message */ + pushl $msg_nested_level_overflow + call sos_display_fatal_error + addl $4, %esp ; jmp 1b + /* Never returns */ + + 2: /* No: all right ! */ + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + + /* Remove fake error code */ + addl $4, %esp + + iret + .endr + +.section ".rodata" +msg_nested_level_overflow: + .string "irq_wrappers.S: IRQ Nested level overflow ! System halted." + +/* Build the sos_irq_wrapper_array, shared with irq.c */ +.p2align 5, 0x0 +sos_irq_wrapper_array: + .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .long (sos_irq_wrapper_\id) + .endr diff --git a/sos-code-article6.5/hwcore/paging.c b/sos-code-article6.5/hwcore/paging.c new file mode 100644 index 0000000..11f3da6 --- /dev/null +++ b/sos-code-article6.5/hwcore/paging.c @@ -0,0 +1,465 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include +#include +#include + +#include "paging.h" + + +/** The structure of a page directory entry. See Intel vol 3 section + 3.6.4 */ +struct x86_pde +{ + sos_ui32_t present :1; /* 1=PT mapped */ + sos_ui32_t write :1; /* 0=read-only, 1=read/write */ + sos_ui32_t user :1; /* 0=supervisor, 1=user */ + sos_ui32_t write_through :1; /* 0=write-back, 1=write-through */ + sos_ui32_t cache_disabled :1; /* 1=cache disabled */ + sos_ui32_t accessed :1; /* 1=read/write access since last clear */ + sos_ui32_t zero :1; /* Intel reserved */ + sos_ui32_t page_size :1; /* 0=4kB, 1=4MB or 2MB (depending on PAE) */ + sos_ui32_t global_page :1; /* Ignored (Intel reserved) */ + sos_ui32_t custom :3; /* Do what you want with them */ + sos_ui32_t pt_paddr :20; +} __attribute__ ((packed)); + + +/** The structure of a page table entry. See Intel vol 3 section + 3.6.4 */ +struct x86_pte +{ + sos_ui32_t present :1; /* 1=PT mapped */ + sos_ui32_t write :1; /* 0=read-only, 1=read/write */ + sos_ui32_t user :1; /* 0=supervisor, 1=user */ + sos_ui32_t write_through :1; /* 0=write-back, 1=write-through */ + sos_ui32_t cache_disabled :1; /* 1=cache disabled */ + sos_ui32_t accessed :1; /* 1=read/write access since last clear */ + sos_ui32_t dirty :1; /* 1=write access since last clear */ + sos_ui32_t zero :1; /* Intel reserved */ + sos_ui32_t global_page :1; /* 1=No TLB invalidation upon cr3 switch + (when PG set in cr4) */ + sos_ui32_t custom :3; /* Do what you want with them */ + sos_ui32_t paddr :20; +} __attribute__ ((packed)); + + +/** Structure of the x86 CR3 register: the Page Directory Base + Register. See Intel x86 doc Vol 3 section 2.5 */ +struct x86_pdbr +{ + sos_ui32_t zero1 :3; /* Intel reserved */ + sos_ui32_t write_through :1; /* 0=write-back, 1=write-through */ + sos_ui32_t cache_disabled :1; /* 1=cache disabled */ + sos_ui32_t zero2 :7; /* Intel reserved */ + sos_ui32_t pd_paddr :20; +} __attribute__ ((packed)); + + +/** + * Helper macro to control the MMU: invalidate the TLB entry for the + * page located at the given virtual address. See Intel x86 vol 3 + * section 3.7. + */ +#define invlpg(vaddr) \ + do { \ + __asm__ __volatile__("invlpg %0"::"m"(*((unsigned *)(vaddr)))); \ + } while(0) + + +/** + * Helper macro to control the MMU: invalidate the whole TLB. See + * Intel x86 vol 3 section 3.7. + */ +#define flush_tlb() \ + do { \ + unsigned long tmpreg; \ + asm volatile("movl %%cr3,%0\n\tmovl %0,%%cr3" :"=r" \ + (tmpreg) : :"memory"); \ + } while (0) + + +/** + * Helper macro to compute the index in the PD for the given virtual + * address + */ +#define virt_to_pd_index(vaddr) \ + (((unsigned)(vaddr)) >> 22) + + +/** + * Helper macro to compute the index in the PT for the given virtual + * address + */ +#define virt_to_pt_index(vaddr) \ + ( (((unsigned)(vaddr)) >> 12) & 0x3ff ) + + +/** + * Helper macro to compute the offset in the page for the given virtual + * address + */ +#define virt_to_page_offset(vaddr) \ + (((unsigned)(vaddr)) & SOS_PAGE_MASK) + + +/** + * Helper function to map a page in the pd.\ Suppose that the RAM + * is identity mapped to resolve PT actual (CPU) address from the PD + * entry + */ +static sos_ret_t paging_setup_map_helper(struct x86_pde * pd, + sos_paddr_t ppage, + sos_vaddr_t vaddr) +{ + /* Get the page directory entry and table entry index for this + address */ + unsigned index_in_pd = virt_to_pd_index(vaddr); + unsigned index_in_pt = virt_to_pt_index(vaddr); + + /* Make sure the page table was mapped */ + struct x86_pte * pt; + if (pd[index_in_pd].present) + { + pt = (struct x86_pte*) (pd[index_in_pd].pt_paddr << 12); + + /* If we allocate a new entry in the PT, increase its reference + count. This test will always be TRUE here, since the setup + routine scans the kernel pages in a strictly increasing + order: at each step, the map will result in the allocation of + a new PT entry. For the sake of clarity, we keep the test + here. */ + if (! pt[index_in_pt].present) + sos_physmem_ref_physpage_at((sos_paddr_t)pt); + + /* The previous test should always be TRUE */ + else + SOS_ASSERT_FATAL(FALSE); /* indicate a fatal error */ + } + else + { + /* No : allocate a new one */ + pt = (struct x86_pte*) sos_physmem_ref_physpage_new(FALSE); + if (! pt) + return -SOS_ENOMEM; + + memset((void*)pt, 0x0, SOS_PAGE_SIZE); + + pd[index_in_pd].present = TRUE; + pd[index_in_pd].write = 1; /* It would be too complicated to + determine whether it + corresponds to a real R/W area + of the kernel code/data or + read-only */ + pd[index_in_pd].pt_paddr = ((sos_paddr_t)pt) >> 12; + } + + + /* Map the page in the page table */ + pt[index_in_pt].present = 1; + pt[index_in_pt].write = 1; /* It would be too complicated to + determine whether it corresponds to + a real R/W area of the kernel + code/data or R/O only */ + pt[index_in_pt].user = 0; + pt[index_in_pt].paddr = ppage >> 12; + + return SOS_OK; +} + + +sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base, + sos_paddr_t identity_mapping_top) +{ + /* The PDBR we will setup below */ + struct x86_pdbr cr3; + + /* Get the PD for the kernel */ + struct x86_pde * pd + = (struct x86_pde*) sos_physmem_ref_physpage_new(FALSE); + + /* The iterator for scanning the kernel area */ + sos_paddr_t paddr; + + /* Reset the PD. For the moment, there is still an IM for the whole + RAM, so that the paddr are also vaddr */ + memset((void*)pd, + 0x0, + SOS_PAGE_SIZE); + + /* Identity-map the identity_mapping_* area */ + for (paddr = identity_mapping_base ; + paddr < identity_mapping_top ; + paddr += SOS_PAGE_SIZE) + { + if (paging_setup_map_helper(pd, paddr, paddr)) + return -SOS_ENOMEM; + } + + /* Identity-map the PC-specific BIOS/Video area */ + for (paddr = BIOS_N_VIDEO_START ; + paddr < BIOS_N_VIDEO_END ; + paddr += SOS_PAGE_SIZE) + { + if (paging_setup_map_helper(pd, paddr, paddr)) + return -SOS_ENOMEM; + } + + /* Ok, kernel is now identity mapped in the PD. We still have to set + up the mirroring */ + pd[virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)].present = TRUE; + pd[virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)].write = 1; + pd[virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)].user = 0; + pd[virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)].pt_paddr + = ((sos_paddr_t)pd)>>12; + + /* We now just have to configure the MMU to use our PD. See Intel + x86 doc vol 3, section 3.6.3 */ + memset(& cr3, 0x0, sizeof(struct x86_pdbr)); /* Reset the PDBR */ + cr3.pd_paddr = ((sos_paddr_t)pd) >> 12; + + /* Actual loading of the PDBR in the MMU: setup cr3 + bits 31[Paging + Enabled] and 16[Write Protect] of cr0, see Intel x86 doc vol 3, + sections 2.5, 3.6.1 and 4.11.3 + note table 4-2 */ + asm volatile ("movl %0,%%cr3\n\t" + "movl %%cr0,%%eax\n\t" + "orl $0x80010000, %%eax\n\t" /* bit 31 | bit 16 */ + "movl %%eax,%%cr0\n\t" + "jmp 1f\n\t" + "1:\n\t" + "movl $2f, %%eax\n\t" + "jmp *%%eax\n\t" + "2:\n\t" ::"r"(cr3):"memory","eax"); + + /* + * Here, the only memory available is: + * - The BIOS+video area + * - the identity_mapping_base .. identity_mapping_top area + * - the PD mirroring area (4M) + * All accesses to other virtual addresses will generate a #PF + */ + + return SOS_OK; +} + + +/* Suppose that the current address is configured with the mirroring + * enabled to access the PD and PT. */ +sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, + sos_vaddr_t vpage_vaddr, + sos_bool_t is_user_page, + sos_ui32_t flags) +{ + /* Get the page directory entry and table entry index for this + address */ + unsigned index_in_pd = virt_to_pd_index(vpage_vaddr); + unsigned index_in_pt = virt_to_pt_index(vpage_vaddr); + + /* Get the PD of the current context */ + struct x86_pde *pd = (struct x86_pde*) + (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)); + + /* Address of the PT in the mirroring */ + struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*index_in_pd); + + /* The mapping of anywhere in the PD mirroring is FORBIDDEN ;) */ + if ((vpage_vaddr >= SOS_PAGING_MIRROR_VADDR) + && (vpage_vaddr < SOS_PAGING_MIRROR_VADDR + SOS_PAGING_MIRROR_SIZE)) + return -SOS_EINVAL; + + /* Map a page for the PT if necessary */ + if (! pd[index_in_pd].present) + { + + /* No : allocate a new one */ + sos_paddr_t pt_ppage + = sos_physmem_ref_physpage_new(! (flags & SOS_VM_MAP_ATOMIC)); + if (! pt_ppage) + { + return -SOS_ENOMEM; + } + + pd[index_in_pd].present = TRUE; + pd[index_in_pd].write = 1; /* Ignored in supervisor mode, see + Intel vol 3 section 4.12 */ + pd[index_in_pd].user = (is_user_page)?1:0; + pd[index_in_pd].pt_paddr = ((sos_paddr_t)pt_ppage) >> 12; + + /* + * The PT is now mapped in the PD mirroring + */ + + /* Invalidate TLB for the page we just added */ + invlpg(pt); + + /* Reset this new PT */ + memset((void*)pt, 0x0, SOS_PAGE_SIZE); + } + + /* If we allocate a new entry in the PT, increase its reference + count. */ + else if (! pt[index_in_pt].present) + sos_physmem_ref_physpage_at(pd[index_in_pd].pt_paddr << 12); + + /* Otherwise, that means that a physical page is implicitely + unmapped */ + else + sos_physmem_unref_physpage(pt[index_in_pt].paddr << 12); + + /* Map the page in the page table */ + pt[index_in_pt].present = TRUE; + pt[index_in_pt].write = (flags & SOS_VM_MAP_PROT_WRITE)?1:0; + pt[index_in_pt].user = (is_user_page)?1:0; + pt[index_in_pt].paddr = ppage_paddr >> 12; + sos_physmem_ref_physpage_at(ppage_paddr); + + /* + * The page is now mapped in the current address space + */ + + /* Invalidate TLB for the page we just added */ + invlpg(vpage_vaddr); + + return SOS_OK; +} + + +sos_ret_t sos_paging_unmap(sos_vaddr_t vpage_vaddr) +{ + sos_ret_t pt_unref_retval; + + /* Get the page directory entry and table entry index for this + address */ + unsigned index_in_pd = virt_to_pd_index(vpage_vaddr); + unsigned index_in_pt = virt_to_pt_index(vpage_vaddr); + + /* Get the PD of the current context */ + struct x86_pde *pd = (struct x86_pde*) + (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)); + + /* Address of the PT in the mirroring */ + struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*index_in_pd); + + /* No page mapped at this address ? */ + if (! pd[index_in_pd].present) + return -SOS_EINVAL; + if (! pt[index_in_pt].present) + return -SOS_EINVAL; + + /* The unmapping of anywhere in the PD mirroring is FORBIDDEN ;) */ + if ((vpage_vaddr >= SOS_PAGING_MIRROR_VADDR) + && (vpage_vaddr < SOS_PAGING_MIRROR_VADDR + SOS_PAGING_MIRROR_SIZE)) + return -SOS_EINVAL; + + /* Reclaim the physical page */ + sos_physmem_unref_physpage(pt[index_in_pt].paddr << 12); + + /* Unmap the page in the page table */ + memset(pt + index_in_pt, 0x0, sizeof(struct x86_pte)); + + /* Invalidate TLB for the page we just unmapped */ + invlpg(vpage_vaddr); + + /* Reclaim this entry in the PT, which may free the PT */ + pt_unref_retval = sos_physmem_unref_physpage(pd[index_in_pd].pt_paddr << 12); + SOS_ASSERT_FATAL(pt_unref_retval >= 0); + if (pt_unref_retval == TRUE) + /* If the PT is now completely unused... */ + { + union { struct x86_pde pde; sos_ui32_t ui32; } u; + + /* + * Reset the PDE + */ + + /* Mark the PDE as unavailable */ + u.ui32 = 0; + + /* Update the PD */ + pd[index_in_pd] = u.pde; + + /* Update the TLB */ + invlpg(pt); + } + + return SOS_OK; +} + + +int sos_paging_get_prot(sos_vaddr_t vaddr) +{ + int retval; + + /* Get the page directory entry and table entry index for this + address */ + unsigned index_in_pd = virt_to_pd_index(vaddr); + unsigned index_in_pt = virt_to_pt_index(vaddr); + + /* Get the PD of the current context */ + struct x86_pde *pd = (struct x86_pde*) + (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)); + + /* Address of the PT in the mirroring */ + struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*index_in_pd); + + /* No page mapped at this address ? */ + if (! pd[index_in_pd].present) + return SOS_VM_MAP_PROT_NONE; + if (! pt[index_in_pt].present) + return SOS_VM_MAP_PROT_NONE; + + /* Default access right of an available page is "read" on x86 */ + retval = SOS_VM_MAP_PROT_READ; + if (pd[index_in_pd].write && pt[index_in_pt].write) + retval |= SOS_VM_MAP_PROT_WRITE; + + return retval; +} + + +sos_paddr_t sos_paging_get_paddr(sos_vaddr_t vaddr) +{ + /* Get the page directory entry and table entry index for this + address */ + unsigned index_in_pd = virt_to_pd_index(vaddr); + unsigned index_in_pt = virt_to_pt_index(vaddr); + unsigned offset_in_page = virt_to_page_offset(vaddr); + + /* Get the PD of the current context */ + struct x86_pde *pd = (struct x86_pde*) + (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*virt_to_pd_index(SOS_PAGING_MIRROR_VADDR)); + + /* Address of the PT in the mirroring */ + struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR + + SOS_PAGE_SIZE*index_in_pd); + + /* No page mapped at this address ? */ + if (! pd[index_in_pd].present) + return (sos_paddr_t)NULL; + if (! pt[index_in_pt].present) + return (sos_paddr_t)NULL; + + return (pt[index_in_pt].paddr << 12) + offset_in_page; +} diff --git a/sos-code-article6.5/hwcore/paging.h b/sos-code-article6.5/hwcore/paging.h new file mode 100644 index 0000000..24b9fc9 --- /dev/null +++ b/sos-code-article6.5/hwcore/paging.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2004 David Decotigny + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_PAGING_H_ +#define _SOS_PAGING_H_ + +/** + * @file paging.h + * + * MMU management routines (arch-dependent). Setup the MMU without + * identity-mapping physical<->virtual addresses over the whole + * physical address space: a single, restricted and known, area is + * identity-mapped, the remaining kernel/user space is not. To access + * and manage the MMU translation tables (PD/PT on x86), we rely on a + * particular configuration, called "mirroring", where the top-level + * translation table (PD on x86) maps itself at a known and fixed (virtual) + * address. The only assumption for this to be possible is that the + * structure of the translation table entries are compatible at the + * different levels of vadddr->paddr translation process (PDE and PTE + * on x86 are Ok). Credits go to Christophe Avoinne for that. + */ + +#include +#include + + +/** + * Basic SOS virtual memory organization + */ +/** Frontier between kernel and user space virtual addresses */ +#define SOS_PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB */ +#define SOS_PAGING_TOP_USER_ADDRESS (0xFFFFFFFF) /* 4GB */ + +/** Length of the space reserved for the mirroring in the kernel + virtual space */ +#define SOS_PAGING_MIRROR_SIZE (1 << 22) /* 1 PD = 1024 Page Tables = 4MB */ + +/** Virtual address where the mirroring takes place */ +#define SOS_PAGING_MIRROR_VADDR \ + (SOS_PAGING_BASE_USER_ADDRESS - SOS_PAGING_MIRROR_SIZE) + + +/** + * sos_paging_map flags + */ +/** Usual virtual memory access rights */ +#define SOS_VM_MAP_PROT_NONE 0 +#define SOS_VM_MAP_PROT_READ (1<<0) +#define SOS_VM_MAP_PROT_WRITE (1<<1) +/* EXEC not supported */ + +/** Mapping a page may involve an physical page allocation (for a new + PT), hence may potentially block */ +#define SOS_VM_MAP_ATOMIC (1<<31) + + +/** + * Setup initial page directory structure where the kernel is + * identically-mapped, and the mirroring. This routine also + * identity-maps the BIOS and video areas, to allow some debugging + * text to be printed to the console. Finally, this routine installs + * the whole configuration into the MMU. + */ +sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base, + sos_paddr_t identity_mapping_top); + +/** + * Map the given physical page at the given virtual address in the + * current address space. + * + * @note *IMPORTANT*: The physical page ppage_paddr *MUST* have been + * referenced by the caller through either a call to + * sos_physmem_ref_physpage_new() or sos_physmem_ref_physpage_at(). It + * would work if this were untrue, but this would be INCORRECT (it is + * expected that one is owning the page before mapping it, or + * otherwise the page could have been stolen by an interrupt or + * another thread). + * + * @param ppage_paddr The address of a physical page (page-aligned) + * @param vpage_vaddr The address of the virtual page (page-aligned) + * @param is_user_page TRUE when the page is available from user space + * @param flags A mask made of SOS_VM_* bits + * + * @note Unless the SOS_VM_MAP_ATOMIC bit is set in the flags, the + * function may potentially block, because a physical page may be + * allocated for a new PT. + */ +sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, + sos_vaddr_t vpage_vaddr, + sos_bool_t is_user_page, + sos_ui32_t flags); + +/** + * Undo the mapping from vaddr to the underlying physical page (if any) + * @param vpage_vaddr The address of the virtual page (page-aligned) + */ +sos_ret_t sos_paging_unmap(sos_vaddr_t vpage_vaddr); + +/** + * Return the page protection flags (SOS_VM_MAP_PROT_*) associated + * with the address, or SOS_VM_MAP_PROT_NONE when page is not mapped + */ +int sos_paging_get_prot(sos_vaddr_t vaddr); + +/** + * Return the physical address of the given virtual address. Since page + * at physical addr 0 is not mapped, the NULL result means "page not + * mapped". + */ +sos_paddr_t sos_paging_get_paddr(sos_vaddr_t vaddr); + +/** + * Tell whether the address is physically mapped + */ +#define sos_paging_check_present(vaddr) \ + (sos_paging_get_paddr(vaddr) != NULL) + +#endif /* _SOS_PAGING_H_ */ diff --git a/sos-code-article6.5/hwcore/segment.h b/sos-code-article6.5/hwcore/segment.h new file mode 100644 index 0000000..4487216 --- /dev/null +++ b/sos-code-article6.5/hwcore/segment.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2004 The SOS Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_HWSEGS_H_ +#define _SOS_HWSEGS_H_ + +/** + * @file segments.h + * + * Global and local (GDT/LDT) segment descriptor definition and + * structure. These segments map virtual addresses (ie + * data/instruction addresses, relative to these segment descriptors) + * to linear addresses (ie addresses in the paged-memory space). + * + * @see Intel x86 doc, vol 3 chapter 3. + */ + + +/* + * Global segment selectors (GDT) for SOS/x86. + * + * @see gdt.h + */ +#define SOS_SEG_NULL 0 /* NULL segment, unused by the procesor */ +#define SOS_SEG_KCODE 1 /* Kernel code segment */ +#define SOS_SEG_KDATA 2 /* Kernel data segment */ + + +#ifndef ASM_SOURCE +/** + * Helper macro that builds a segment register's value + */ +#define SOS_BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \ + ( (((desc_privilege) & 0x3) << 0) \ + | (((in_ldt)?1:0) << 2) \ + | ((seg_index) << 3) ) +#else +/* + * Assembler-compliant version. + * + * Caution: In assembler code, "in_ldt" MUST be either 1 or 0, nothing + * else. + */ +#define SOS_BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \ + ( (((desc_privilege) & 0x3) << 0) \ + | ((in_ldt & 1) << 2) \ + | ((seg_index) << 3) ) +#endif + + +/* + * Local segment selectors (LDT) for SOS/x86 + */ +/* None */ + +#endif /* _SOS_HWSEGS_H_ */ -- cgit v1.2.3