summaryrefslogtreecommitdiff
path: root/sos-code-article6.5/hwcore
diff options
context:
space:
mode:
authorAlex AUVOLAT <alex.auvolat@ens.fr>2014-03-28 17:09:15 +0100
committerAlex AUVOLAT <alex.auvolat@ens.fr>2014-03-28 17:09:15 +0100
commita8968330aff45e0b8cf278f49fa337d5fcb9bfd8 (patch)
treededf697b1a5c3c96e77f77e551e9e6af8785a51c /sos-code-article6.5/hwcore
parent8d9e22df8afa4c3339e52c7b3b77388ca0e69fac (diff)
downloadSOS-a8968330aff45e0b8cf278f49fa337d5fcb9bfd8.tar.gz
SOS-a8968330aff45e0b8cf278f49fa337d5fcb9bfd8.zip
Import and compile code for article 6.5
Diffstat (limited to 'sos-code-article6.5/hwcore')
-rw-r--r--sos-code-article6.5/hwcore/cpu_context.c407
-rw-r--r--sos-code-article6.5/hwcore/cpu_context.h270
-rw-r--r--sos-code-article6.5/hwcore/cpu_context_switch.S128
-rw-r--r--sos-code-article6.5/hwcore/exception.c167
-rw-r--r--sos-code-article6.5/hwcore/exception.h83
-rw-r--r--sos-code-article6.5/hwcore/exception_wrappers.S221
-rw-r--r--sos-code-article6.5/hwcore/gdt.c152
-rw-r--r--sos-code-article6.5/hwcore/gdt.h42
-rw-r--r--sos-code-article6.5/hwcore/i8254.c79
-rw-r--r--sos-code-article6.5/hwcore/i8254.h35
-rw-r--r--sos-code-article6.5/hwcore/i8259.c79
-rw-r--r--sos-code-article6.5/hwcore/i8259.h40
-rw-r--r--sos-code-article6.5/hwcore/idt.c159
-rw-r--r--sos-code-article6.5/hwcore/idt.h84
-rw-r--r--sos-code-article6.5/hwcore/ioports.h46
-rw-r--r--sos-code-article6.5/hwcore/irq.c100
-rw-r--r--sos-code-article6.5/hwcore/irq.h97
-rw-r--r--sos-code-article6.5/hwcore/irq_wrappers.S220
-rw-r--r--sos-code-article6.5/hwcore/paging.c465
-rw-r--r--sos-code-article6.5/hwcore/paging.h132
-rw-r--r--sos-code-article6.5/hwcore/segment.h70
21 files changed, 3076 insertions, 0 deletions
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 <sos/assert.h>
+#include <sos/klibc.h>
+#include <drivers/bochs.h>
+#include <drivers/x86_videomem.h>
+#include <hwcore/segment.h>
+
+#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 <sos/types.h>
+#include <sos/errno.h>
+
+
+/**
+ * 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 <sos/assert.h>
+#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 <sos/errno.h>
+# 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 <sos/types.h>
+#include <sos/errno.h>
+
+/**
+ * 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 <hwcore/ioports.h>
+
+#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 <sos/errno.h>
+
+/**
+ * @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 <sos/errno.h>
+
+/**
+ * @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 <sos/errno.h>
+#include <sos/types.h>
+
+/* 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 <sos/errno.h>
+#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 <sos/physmem.h>
+#include <sos/klibc.h>
+#include <sos/assert.h>
+
+#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 <sos/types.h>
+#include <sos/errno.h>
+
+
+/**
+ * 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_ */