diff -ruN /tmp/sos-code-article6.75/drivers/bochs.c ../sos-code-article7/drivers/bochs.c
--- /tmp/sos-code-article6.75/drivers/bochs.c	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/drivers/bochs.c	2005-04-27 20:14:16.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/drivers/bochs.h ../sos-code-article7/drivers/bochs.h
--- /tmp/sos-code-article6.75/drivers/bochs.h	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/drivers/bochs.h	2005-04-27 20:14:16.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/drivers/x86_videomem.c ../sos-code-article7/drivers/x86_videomem.c
--- /tmp/sos-code-article6.75/drivers/x86_videomem.c	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/drivers/x86_videomem.c	2005-04-27 20:14:16.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/drivers/x86_videomem.h ../sos-code-article7/drivers/x86_videomem.h
--- /tmp/sos-code-article6.75/drivers/x86_videomem.h	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/drivers/x86_videomem.h	2005-04-27 20:14:16.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/extra/dot.mkvars ../sos-code-article7/extra/dot.mkvars
--- /tmp/sos-code-article6.75/extra/dot.mkvars	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/extra/dot.mkvars	2005-04-27 20:14:17.000000000 +0200
@@ -6,7 +6,8 @@
 CC := i586-gnu-gcc
 LD := i586-gnu-ld
 OBJCOPY := i586-gnu-objcopy
-CFLAGS += -O3
+CFLAGS += -g
+LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
 
 # Configuration of mtools
 MTOOLSRC = extra/mtoolsrc
diff -ruN /tmp/sos-code-article6.75/hwcore/cpu_context.c ../sos-code-article7/hwcore/cpu_context.c
--- /tmp/sos-code-article6.75/hwcore/cpu_context.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/cpu_context.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,5 @@
-/* Copyright (C) 2000-2004, The KOS team
-   Copyright (C) 1999  Free Software Foundation, Inc.
+/* 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
@@ -23,6 +23,8 @@
 #include <drivers/bochs.h>
 #include <drivers/x86_videomem.h>
 #include <hwcore/segment.h>
+#include <hwcore/gdt.h>
+#include <sos/uaccess.h>
 
 #include "cpu_context.h"
 
@@ -40,7 +42,7 @@
  * the registers are stored on the stack in
  * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.
  */
-struct sos_cpu_kstate {
+struct sos_cpu_state {
   /* (Lower addresses) */
 
   /* These are SOS convention */
@@ -48,7 +50,9 @@
   sos_ui16_t  fs;
   sos_ui16_t  es;
   sos_ui16_t  ds;
-  sos_ui16_t  ss;
+  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;
@@ -61,13 +65,184 @@
   /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
   sos_ui32_t  error_code;
   sos_vaddr_t eip;
-  sos_ui32_t  cs;
+  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));
+
+
+/**
+ * Structure of an interrupted User thread's context. This is almost
+ * the same as a kernel context, except that 2 additional values are
+ * pushed on the stack before the eflags/cs/eip of the interrupted
+ * context: the stack configuration of the interrupted user context.
+ *
+ * @see Section 6.4.1 of Intel x86 vol 1
+ */
+struct sos_cpu_ustate
+{
+  struct sos_cpu_state regs;
+  struct
+  {
+    sos_ui32_t cpl3_esp;
+    sos_ui16_t cpl3_ss;
+  };
+} __attribute__((packed));
+
+
+/*
+ * Structure of a Task State Segment on the x86 Architecture.
+ *
+ * @see Intel x86 spec vol 3, figure 6-2
+ *
+ * @note Such a data structure should not cross any page boundary (see
+ * end of section 6.2.1 of Intel spec vol 3). This is the reason why
+ * we tell gcc to align it on a 128B boundary (its size is 104B, which
+ * is <= 128).
+ */
+struct x86_tss {
+
+  /**
+   * Intel provides a way for a task to switch to another in an
+   * automatic way (call gates). In this case, the back_link field
+   * stores the source TSS of the context switch. This allows to
+   * easily implement coroutines, task backtracking, ... In SOS we
+   * don't use TSS for the context switch purpouse, so we always
+   * ignore this field.
+   * (+0)
+   */
+  sos_ui16_t back_link;
+
+  sos_ui16_t reserved1;
+
+  /* CPL0 saved context. (+4) */
+  sos_vaddr_t esp0;
+  sos_ui16_t ss0;
+
+  sos_ui16_t reserved2;
+
+  /* CPL1 saved context. (+12) */
+  sos_vaddr_t esp1;
+  sos_ui16_t ss1;
+
+  sos_ui16_t reserved3;
+
+  /* CPL2 saved context. (+20) */
+  sos_vaddr_t esp2;
+  sos_ui16_t ss2;
+
+  sos_ui16_t reserved4;
+
+  /* Interrupted context's saved registers. (+28) */
+  sos_vaddr_t cr3;
+  sos_vaddr_t eip;
+  sos_ui32_t eflags;
+  sos_ui32_t eax;
+  sos_ui32_t ecx;
+  sos_ui32_t edx;
+  sos_ui32_t ebx;
+  sos_ui32_t esp;
+  sos_ui32_t ebp;
+  sos_ui32_t esi;
+  sos_ui32_t edi;
+
+  /* +72 */
+  sos_ui16_t es;
+  sos_ui16_t reserved5;
+
+  /* +76 */
+  sos_ui16_t cs;
+  sos_ui16_t reserved6;
+
+  /* +80 */
+  sos_ui16_t ss;
+  sos_ui16_t reserved7;
+
+  /* +84 */
+  sos_ui16_t ds;
+  sos_ui16_t reserved8;
+
+  /* +88 */
+  sos_ui16_t fs;
+  sos_ui16_t reserved9;
+
+  /* +92 */
+  sos_ui16_t gs;
+  sos_ui16_t reserved10;
+
+  /* +96 */
+  sos_ui16_t ldtr;
+  sos_ui16_t reserved11;
+
+  /* +100 */
+  sos_ui16_t debug_trap_flag :1;
+  sos_ui16_t reserved12      :15;
+  sos_ui16_t iomap_base_addr;
+
+  /* 104 */
+} __attribute__((packed, aligned(128)));
+
+
+static struct x86_tss kernel_tss;
+
+
+sos_ret_t sos_cpu_context_subsystem_setup()
+{
+  /* Reset the kernel TSS */
+  memset(&kernel_tss, 0x0, sizeof(kernel_tss));
+
+  /**
+   * Now setup the kernel TSS.
+   *
+   * Considering the privilege change method we choose (cpl3 -> cpl0
+   * through a software interrupt), we don't need to initialize a
+   * full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually,
+   * only a correct value for the kernel esp and ss are required (aka
+   * "ss0" and "esp0" fields). Since the esp0 will have to be updated
+   * at privilege change time, we don't have to set it up now.
+   */
+  kernel_tss.ss0 = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA);
+
+  /* Register this TSS into the gdt */
+  sos_gdt_register_kernel_tss((sos_vaddr_t) &kernel_tss);
+
+  return SOS_OK;
+}
+
+
+/**
+ * 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,
@@ -87,7 +262,7 @@
 }
 
 
-sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt,
+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,
@@ -95,6 +270,9 @@
 			      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 ! */
@@ -121,10 +299,10 @@
   sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr;
 
   /* If needed, poison the stack */
-#ifdef SOS_CPU_KSTATE_DETECT_UNINIT_VARS
-  memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, stack_size);
-#elif defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW)
-  sos_cpu_kstate_prepare_detect_stack_overflow(stack_bottom, stack_size);
+#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
@@ -144,94 +322,263 @@
   /* 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);
-  *ctxt = (struct sos_cpu_kstate*)tmp_vaddr;
+  kctxt = (struct sos_cpu_kstate*)tmp_vaddr;
 
   /* Initialize the CPU context structure */
-  memset(*ctxt, 0x0, sizeof(struct sos_cpu_kstate));
+  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 */
-  (*ctxt)->eip = (sos_ui32_t)core_routine;
+  kctxt->regs.eip = (sos_ui32_t)core_routine;
 
   /* Setup the segment registers */
-  (*ctxt)->cs  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KCODE); /* Code */
-  (*ctxt)->ds  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */
-  (*ctxt)->es  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */
-  (*ctxt)->ss  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Stack */
+  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 */
-  (*ctxt)->eflags = (1 << 9); /* set IF bit */
+  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_KSTATE_DETECT_STACK_OVERFLOW)
+sos_ret_t sos_cpu_ustate_init(struct sos_cpu_state **ctxt,
+			      sos_uaddr_t  user_start_PC,
+			      sos_ui32_t   user_start_arg1,
+			      sos_ui32_t   user_start_arg2,
+			      sos_uaddr_t  user_initial_SP,
+			      sos_vaddr_t  kernel_stack_bottom,
+			      sos_size_t   kernel_stack_size)
+{
+  /* We are initializing a User thread's context */
+  struct sos_cpu_ustate *uctxt;
+
+  /* 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, etc. are allways VALID ! */
+
+  /* Compute the address of the CPU state to restore on CPU when
+     switching to this new user thread */
+  sos_vaddr_t uctxt_vaddr = kernel_stack_bottom
+                             + kernel_stack_size
+                             - sizeof(struct sos_cpu_ustate);
+  uctxt = (struct sos_cpu_ustate*)uctxt_vaddr;
+
+  /* If needed, poison the kernel stack */
+#ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS
+  memset((void*)kernel_stack_bottom,
+	 SOS_CPU_STATE_STACK_POISON,
+	 kernel_stack_size);
+#elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
+  sos_cpu_state_prepare_detect_kernel_stack_overflow(kernel_stack_bottom,
+						     kernel_stack_size);
+#endif
+
+  /*
+   * Setup the initial context structure, so that the CPU will restore
+   * the initial registers' value for the user thread. The
+   * user thread argument is passed in the EAX register.
+   */
+
+  memset(uctxt, 0x0, sizeof(struct sos_cpu_ustate));
+  
+  /* Tell the CPU context structure that the first instruction to
+     execute will be located at user_start_PC (in user space) */
+  uctxt->regs.eip = (sos_ui32_t)user_start_PC;
+  
+  /* Tell the CPU where will be the user stack */
+  uctxt->cpl3_esp = user_initial_SP;
+
+  /* The parameter to the start function is not passed by the stack to
+     avoid a possible page fault */
+  uctxt->regs.eax = user_start_arg1;
+  uctxt->regs.ebx = user_start_arg2;
+
+  /* Setup the segment registers */
+  uctxt->regs.cs
+    = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE); /* Code */
+  uctxt->regs.ds
+    = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */
+  uctxt->regs.es
+    = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */
+  uctxt->cpl3_ss
+    = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* User Stack */
+
+  /* We need also to update the segment for the kernel stack
+     segment. It will be used when this context will be restored on
+     CPU: initially it will be executing in kernel mode and will
+     switch immediatly to user mode */
+  uctxt->regs.cpl0_ss
+    = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Kernel Stack */
+
+  /* fs and gs unused for the moment. */
+
+  /* The newly created context is initially interruptible */
+  uctxt->regs.eflags = (1 << 9); /* set IF bit */
+
+  /* Finally, update the generic kernel/user thread context */
+  *ctxt = (struct sos_cpu_state*) uctxt;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_cpu_context_is_in_user_mode(const struct sos_cpu_state *ctxt)
+{
+  /* An interrupted user thread has its CS register set to that of the
+     User code segment */
+  switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs))
+    {
+    case SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE):
+      return TRUE;
+      break;
+
+    case SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE):
+      return FALSE;
+      break;
+
+    default:
+      SOS_FATAL_ERROR("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !",
+		      (unsigned) GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
+		      SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE),
+		      SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE));
+      break;
+    }
+
+  /* Should never get here */
+  return -SOS_EFATAL;
+}
+
+
+#if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
 void
-sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
-					     sos_vaddr_t stack_bottom,
-					     sos_size_t stack_size)
+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_KSTATE_DETECT_STACK_OVERFLOW;
+  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_KSTATE_STACK_POISON, poison_size);
+  memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, poison_size);
 }
 
 
 void
-sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
-				     sos_vaddr_t stack_bottom,
-				     sos_size_t stack_size)
+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_KSTATE_DETECT_STACK_OVERFLOW) && (i < stack_size) ;
+       (i < SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) && (i < stack_size) ;
        c++, i++)
     {
-      SOS_ASSERT_FATAL(SOS_CPU_KSTATE_STACK_POISON == *c);
+      SOS_ASSERT_FATAL(SOS_CPU_STATE_STACK_POISON == *c);
     }
 }
 #endif
 
 
-sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt)
+/* =======================================================================
+ * 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_kstate_get_SP(const struct sos_cpu_kstate *ctxt)
+sos_vaddr_t sos_cpu_context_get_SP(const struct sos_cpu_state *ctxt)
 {
   SOS_ASSERT_FATAL(NULL != ctxt);
+
+  /* 'ctxt' corresponds to the SP of the interrupted context, in Kernel
+     mode. We have to test whether the original interrupted context
+     was that of a kernel or user thread */
+  if (TRUE == sos_cpu_context_is_in_user_mode(ctxt))
+    {
+      struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt;
+      return uctxt->cpl3_esp;
+    }
+
+  /* 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_kstate_dump(const struct sos_cpu_kstate *ctxt)
+sos_ret_t
+sos_cpu_context_set_EX_return_address(struct sos_cpu_state *ctxt,
+				      sos_vaddr_t ret_vaddr)
+{
+  ctxt->eip = ret_vaddr;
+  return SOS_OK;
+}
+
+
+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",
+	   "CPU: eip=%x esp0=%x eflags=%x cs=%x ds=%x ss0=%x err=%x",
 	   (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags,
-	   (unsigned)ctxt->cs, (unsigned)ctxt->ds, (unsigned)ctxt->ss,
+	   (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
+	   (unsigned)ctxt->cpl0_ss,
 	   (unsigned)ctxt->error_code);
+  if (TRUE == sos_cpu_context_is_in_user_mode(ctxt))
+    {
+      struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt;
+      snprintf(buf, sizeof(buf),
+	       "%s esp3=%x ss3=%x",
+	       buf, (unsigned)uctxt->cpl3_esp, (unsigned)uctxt->cpl3_ss);
+    }
+  else
+    snprintf(buf, sizeof(buf), "%s [KERNEL MODE]", buf);
+
   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);
+			     SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY,
+			     buf);
 }
 
 
-sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt)
+/* =======================================================================
+ * 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;
@@ -239,12 +586,23 @@
 
 
 sos_vaddr_t
-sos_cpu_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *ctxt)
+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 */
+  /*
+   * 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)
 		: );
@@ -253,7 +611,237 @@
 }
 
 
-sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate,
+/* =======================================================================
+ * Public Accessor functions TO BE USED ONLY BY the SYSCALL handler
+ */
+
+
+/*
+ * By convention, the USER SOS programs always pass 4 arguments to the
+ * kernel syscall handler: in eax/../edx. For less arguments, the
+ * unused registers are filled with 0s. For more arguments, the 4th
+ * syscall parameter gives the address of the array containing the
+ * remaining arguments. In any case, eax corresponds to the syscall
+ * IDentifier.
+ */
+
+
+inline
+sos_ret_t sos_syscall_get3args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3)
+{
+  *arg1 = user_ctxt->ebx;
+  *arg2 = user_ctxt->ecx;
+  *arg3 = user_ctxt->edx; 
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_syscall_get1arg(const struct sos_cpu_state *user_ctxt,
+			      /* out */unsigned int *arg1)
+{
+  unsigned int unused;
+  return sos_syscall_get3args(user_ctxt, arg1, & unused, & unused);
+}
+
+
+sos_ret_t sos_syscall_get2args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2)
+{
+  unsigned int unused;
+  return sos_syscall_get3args(user_ctxt, arg1, arg2, & unused);
+}
+
+
+/*
+ * sos_syscall_get3args() is defined in cpu_context.c because it needs
+ * to know the structure of a struct spu_state
+ */
+
+sos_ret_t sos_syscall_get4args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4)
+{
+  sos_uaddr_t  uaddr_other_args;
+  unsigned int other_args[2];
+  sos_ret_t    retval;
+
+  /* Retrieve the 3 arguments. The last one is an array containing the
+     remaining arguments */
+  retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
+				(unsigned int *)& uaddr_other_args);
+  if (SOS_OK != retval)
+    return retval;
+  
+  /* Copy the array containing the remaining arguments from user
+     space */
+  retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
+				(sos_uaddr_t)uaddr_other_args,
+				sizeof(other_args));
+  if (sizeof(other_args) != retval)
+    return -SOS_EFAULT;
+
+  *arg3 = other_args[0];
+  *arg4 = other_args[1];
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_syscall_get5args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5)
+{
+  sos_uaddr_t  uaddr_other_args;
+  unsigned int other_args[3];
+  sos_ret_t    retval;
+
+  /* Retrieve the 3 arguments. The last one is an array containing the
+     remaining arguments */
+  retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
+				(unsigned int *)& uaddr_other_args);
+  if (SOS_OK != retval)
+    return retval;
+  
+  /* Copy the array containing the remaining arguments from user
+     space */
+  retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
+				(sos_uaddr_t)uaddr_other_args,
+				sizeof(other_args));
+  if (sizeof(other_args) != retval)
+    return -SOS_EFAULT;
+
+  *arg3 = other_args[0];
+  *arg4 = other_args[1];
+  *arg5 = other_args[2];
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_syscall_get6args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6)
+{
+  sos_uaddr_t  uaddr_other_args;
+  unsigned int other_args[4];
+  sos_ret_t    retval;
+
+  /* Retrieve the 3 arguments. The last one is an array containing the
+     remaining arguments */
+  retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
+				(unsigned int *)& uaddr_other_args);
+  if (SOS_OK != retval)
+    return retval;
+  
+  /* Copy the array containing the remaining arguments from user
+     space */
+  retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
+				(sos_uaddr_t)uaddr_other_args,
+				sizeof(other_args));
+  if (sizeof(other_args) != retval)
+    return -SOS_EFAULT;
+
+  *arg3 = other_args[0];
+  *arg4 = other_args[1];
+  *arg5 = other_args[2];
+  *arg6 = other_args[3];
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_syscall_get7args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6,
+			       /* out */unsigned int *arg7)
+{
+  sos_uaddr_t  uaddr_other_args;
+  unsigned int other_args[5];
+  sos_ret_t    retval;
+
+  /* Retrieve the 3 arguments. The last one is an array containing the
+     remaining arguments */
+  retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
+				(unsigned int *)& uaddr_other_args);
+  if (SOS_OK != retval)
+    return retval;
+  
+  /* Copy the array containing the remaining arguments from user
+     space */
+  retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
+				(sos_uaddr_t)uaddr_other_args,
+				sizeof(other_args));
+  if (sizeof(other_args) != retval)
+    return -SOS_EFAULT;
+
+  *arg3 = other_args[0];
+  *arg4 = other_args[1];
+  *arg5 = other_args[2];
+  *arg6 = other_args[3];
+  *arg7 = other_args[4];
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_syscall_get8args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6,
+			       /* out */unsigned int *arg7,
+			       /* out */unsigned int *arg8)
+{
+  sos_uaddr_t  uaddr_other_args;
+  unsigned int other_args[6];
+  sos_ret_t    retval;
+
+  /* Retrieve the 3 arguments. The last one is an array containing the
+     remaining arguments */
+  retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
+				(unsigned int *)& uaddr_other_args);
+  if (SOS_OK != retval)
+    return retval;
+  
+  /* Copy the array containing the remaining arguments from user
+     space */
+  retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
+				(sos_uaddr_t)uaddr_other_args,
+				sizeof(other_args));
+  if (sizeof(other_args) != retval)
+    return -SOS_EFAULT;
+
+  *arg3 = other_args[0];
+  *arg4 = other_args[1];
+  *arg5 = other_args[2];
+  *arg6 = other_args[3];
+  *arg7 = other_args[4];
+  *arg8 = other_args[5];
+  return SOS_OK;
+}
+
+
+/* =======================================================================
+ * 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,
@@ -263,6 +851,14 @@
   int depth;
   sos_vaddr_t callee_PC, caller_frame;
 
+  /* Cannot backtrace an interrupted user thread ! */
+  if ((NULL != cpu_state)
+      &&
+      (TRUE == sos_cpu_context_is_in_user_mode(cpu_state)))
+    {
+      return 0;
+    }
+  
   /*
    * Layout of a frame on the x86 (compiler=gcc):
    *
@@ -292,10 +888,10 @@
    * function will return -SOS_ENOSUP.
    */
 
-  if (cpu_kstate)
+  if (cpu_state)
     {
-      callee_PC    = cpu_kstate->eip;
-      caller_frame = cpu_kstate->ebp;
+      callee_PC    = cpu_state->eip;
+      caller_frame = cpu_state->ebp;
     }
   else
     {
@@ -321,3 +917,43 @@
   
   return depth;
 }
+
+
+/* *************************************************************
+ * Function to manage the TSS.  This function is not really "public":
+ * it is reserved to the assembler routines defined in
+ * cpu_context_switch.S
+ *
+ * Update the kernel stack address so that the IRQ, syscalls and
+ * exception return in a correct stack location when coming back into
+ * kernel mode.
+ */
+void
+sos_cpu_context_update_kernel_tss(struct sos_cpu_state *next_ctxt)
+{
+  /* next_ctxt corresponds to an interrupted user thread ? */
+  if (sos_cpu_context_is_in_user_mode(next_ctxt))
+    {
+      /*
+       * Yes: "next_ctxt" is an interrupted user thread => we are
+       * going to switch to user mode ! Setup the stack address so
+       * that the user thread "next_ctxt" can come back to the correct
+       * stack location when returning in kernel mode.
+       *
+       * This stack location corresponds to the SP of the next user
+       * thread once its context has been transferred on the CPU, ie
+       * once the CPU has executed all the pop/iret instruction of the
+       * context switch with privilege change.
+       */
+      kernel_tss.esp0 = ((sos_vaddr_t)next_ctxt)
+	                + sizeof(struct sos_cpu_ustate);
+      /* Note: no need to protect this agains IRQ because IRQs are not
+	 allowed to update it by themselves, and they are not allowed
+	 to block */
+    }
+  else
+    {
+      /* No: No need to update kernel TSS when we stay in kernel
+	 mode */
+    }
+}
diff -ruN /tmp/sos-code-article6.75/hwcore/cpu_context.h ../sos-code-article7/hwcore/cpu_context.h
--- /tmp/sos-code-article6.75/hwcore/cpu_context.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/cpu_context.h	2005-04-27 20:14:18.000000000 +0200
@@ -1,5 +1,5 @@
-/* Copyright (C) 2000-2004, The KOS team
-   Copyright (C) 1999  Free Software Foundation, Inc.
+/* 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
@@ -32,19 +32,26 @@
 
 
 /**
- * Opaque structure storing the CPU context of an inactive kernel
- * thread, as saved by the low level primitives below or by the
+ * Prepare the system to deal with multiple CPU execution contexts
+ */
+sos_ret_t sos_cpu_context_subsystem_setup();
+
+
+/**
+ * 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_kstate;
+struct sos_cpu_state;
 
 
 /**
- * The type of the functions passed as arguments below
+ * 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));
 
@@ -56,9 +63,9 @@
  * start_func function returns, the function exit_func is called with
  * argument exit_arg.
  *
- * @param ctxt The kernel thread CPU context to initialize. The
- * address of the newly-initialized struct sos_cpu_kstate will be
- * stored in this variable. The contents of this struct sos_cpu_kstate
+ * @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
@@ -83,7 +90,7 @@
  *
  * @note the newly created context is INTERRUPTIBLE by default !
  */
-sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt,
+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,
@@ -93,27 +100,68 @@
 
 
 /**
- * Function that performs an immediate context-switch from one kernel
- * thread to another one. It stores the current executing context in
- * from_ctxt, and restores to_context on CPU.
+ * Function to create an initial context for a user thread starting
+ * its execution at function user_start_PC with the user_start_arg
+ * argument. The address of the user stack before any modification by
+ * the ustate_init() function is given by user_start_SP. The user
+ * thread starts in user space first and needs a kernel stack for
+ * the syscalls and for handling interrupts: the address of this
+ * kernel stack is given by the kernel_stack_* parameters.
+ *
+ * @param uctxt The user 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 kernel stack of the thread.
+ *
+ * @param user_start_PC The address of the first instruction that will
+ * be executed in user mode 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 user_start_SP The initial user stack address.
+ *
+ * @param user_start_argX The 2 parameters passed to the initial user
+ * thread function (in registers).
+ *
+ * @param kernel_stack_bottom The lowest address of the kernel stack
+ * used to switch to user mode and to handle interrupts/exceptions.
+ *
+ * @param kernel_stack_size The size of the kernel stack (@see
+ * kernel_stack_bottom).
+ *
+ * @note the newly thread context is INTERRUPTIBLE !
+ */
+sos_ret_t sos_cpu_ustate_init(struct sos_cpu_state **uctxt,
+			      sos_uaddr_t  user_start_PC,
+			      sos_ui32_t   user_start_arg1,
+			      sos_ui32_t   user_start_arg2,
+			      sos_uaddr_t  user_initial_SP,
+			      sos_vaddr_t  kernel_stack_bottom,
+			      sos_size_t   kernel_stack_size);
+
+
+/**
+ * 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_kstate will be
+ * @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_kstate located at this address. Must NOT be NULL.
+ * sos_cpu_state located at this address. Must NOT be NULL.
  */
-void sos_cpu_kstate_switch(struct sos_cpu_kstate **from_ctxt,
-			   struct sos_cpu_kstate *to_ctxt);
+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 thread) without saving
- * the old context (of another kernel 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.
+ * 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
  *
@@ -122,31 +170,41 @@
  * context to switch_to_ctxt.
  */
 void
-sos_cpu_kstate_exit_to(struct sos_cpu_kstate *switch_to_ctxt,
-		       sos_cpu_kstate_function_arg1_t *reclaiming_func,
-		       sos_ui32_t reclaiming_arg) __attribute__((noreturn));
-
+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 whether the saved context was in kernel or user context
+ *
+ * @return TRUE when context was interrupted when in user mode, FALSE
+ * when in kernel mode, < 0 on error.
+ */
+sos_ret_t
+sos_cpu_context_is_in_user_mode(const struct sos_cpu_state *ctxt);
+
+
 /**
- * Return Program Counter stored in the saved context
+ * Return Program Counter stored in the saved kernel/user context
  */
-sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt);
+sos_vaddr_t sos_cpu_context_get_PC(const struct sos_cpu_state *ctxt);
 
 
 /**
- * Return Stack Pointer stored in the saved context
+ * Return Stack Pointer stored in the saved kernel/user context
  */
-sos_vaddr_t sos_cpu_kstate_get_SP(const struct sos_cpu_kstate *ctxt);
+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_kstate_dump(const struct sos_cpu_kstate *ctxt);
+void sos_cpu_context_dump(const struct sos_cpu_state *ctxt);
 
 
 /* =======================================================================
@@ -158,14 +216,88 @@
  * Return the argument passed by the CPU upon exception, as stored in the
  * saved context
  */
-sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt);
+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_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *ctxt);
+sos_cpu_context_get_EX_faulting_vaddr(const struct sos_cpu_state *ctxt);
+
+
+/**
+ * Change the return address of the given context
+ */
+sos_ret_t
+sos_cpu_context_set_EX_return_address(struct sos_cpu_state *ctxt,
+				      sos_vaddr_t ret_vaddr);
+
+
+/* =======================================================================
+ * Public Accessor functions TO BE USED ONLY BY the SYSCALL handler
+ */
+
+/**
+ * Low-level functions used by the syscall handler. They are
+ * responsible for retrieving the arguments passed to the syscall when
+ * a user thread makes a syscall. Some of these arguments are
+ * available as registers' values in the user context, some of them
+ * are user-space addresses given by these registers.
+ *
+ * @return SOS_OK on success, <0 otherwise
+ */
+sos_ret_t sos_syscall_get1arg(const struct sos_cpu_state *user_ctxt,
+			      /* out */unsigned int *arg1);
+
+sos_ret_t sos_syscall_get2args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2);
+
+sos_ret_t sos_syscall_get3args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3);
+
+sos_ret_t sos_syscall_get4args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4);
+
+sos_ret_t sos_syscall_get5args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5);
+
+sos_ret_t sos_syscall_get6args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6);
+
+sos_ret_t sos_syscall_get7args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6,
+			       /* out */unsigned int *arg7);
+
+sos_ret_t sos_syscall_get8args(const struct sos_cpu_state *user_ctxt,
+			       /* out */unsigned int *arg1,
+			       /* out */unsigned int *arg2,
+			       /* out */unsigned int *arg3,
+			       /* out */unsigned int *arg4,
+			       /* out */unsigned int *arg5,
+			       /* out */unsigned int *arg6,
+			       /* out */unsigned int *arg7,
+			       /* out */unsigned int *arg8);
 
 
 /* =======================================================================
@@ -175,35 +307,35 @@
  *  - when the thread might have gone too deep in the stack
  */
 /** The signature of the poison */
-#define SOS_CPU_KSTATE_STACK_POISON 0xa5
+#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_KSTATE_DETECT_UNINIT_VARS
-/* #undef SOS_CPU_KSTATE_DETECT_UNINIT_VARS */
+#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_KSTATE_DETECT_STACK_OVERFLOW  64
-/* #undef SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW */
+#define SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW  64
+/* #undef SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW */
 
-#if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW)
+#if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
 void
-sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
-					     sos_vaddr_t stack_bottom,
-					     sos_size_t stack_size);
-void sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
-					  sos_vaddr_t stack_bottom,
-					  sos_size_t stack_size);
+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_kstate_prepare_detect_stack_overflow(ctxt,stkbottom,stksize) \
+# define sos_cpu_state_prepare_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \
   ({ /* nop */ })
-# define sos_cpu_kstate_detect_stack_overflow(ctxt,stkbottom,stksize) \
+# define sos_cpu_state_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \
   ({ /* nop */ })
 #endif
 
@@ -235,10 +367,11 @@
 
 
 /**
- * Call the backtracer callback on each frame stored in the cpu_kstate
+ * Call the backtracer callback on each frame stored in the cpu_state
  *
- * @param cpu_kstate The CPU context we want to explore. NULL to
- * backtrace the current CPU context.
+ * @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
  *
@@ -258,7 +391,7 @@
  * @note Might be inaccurate when gcc's -fomit-frame-pointer has been
  * used.
  */
-sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate,
+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,
diff -ruN /tmp/sos-code-article6.75/hwcore/cpu_context_switch.S ../sos-code-article7/hwcore/cpu_context_switch.S
--- /tmp/sos-code-article6.75/hwcore/cpu_context_switch.S	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/cpu_context_switch.S	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,5 @@
-/* Copyright (C) 2000-2004, The KOS team
-   Copyright (C) 1999  Free Software Foundation, Inc.
+/* 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
@@ -17,15 +17,31 @@
    USA. 
 */
 #define ASM_SOURCE 1
+
+
+#include "segment.h"
+	
          
 .file "cpu_context_switch.S"
 
 .text
 
+
+/**
+ * C Function called by the routines below in order to tell the CPU
+ * where will be the kernel stack (needed by the interrupt handlers)
+ * when next_ctxt will come back into kernel mode.
+ *
+ * void sos_cpu_context_update_kernel_tss(struct sos_cpu_state *next_ctxt)
+ *
+ * @see end of cpu_context.c
+ */
+.extern sos_cpu_context_update_kernel_tss
+
 	
-.globl sos_cpu_kstate_switch
-.type sos_cpu_kstate_switch, @function
-sos_cpu_kstate_switch:
+.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
@@ -48,7 +64,7 @@
   pushw %gs          //              esp
 
   /*
-   * Now that the original eax/ebx are store, we can use them safely
+   * Now that the original eax/ebx are stored, we can use them safely
    */
 	
   /* Store the address of the saved context */
@@ -58,6 +74,14 @@
   /* This is the proper context switch ! We change the stack here */
   movl 64(%esp), %esp
 
+  /* Prepare kernel TSS in case we are switching to a user thread: we
+     make sure that we will come back into the kernel at a correct
+     stack location */
+  pushl %esp /* Pass the location of the context we are
+	        restoring to the function */
+  call sos_cpu_context_update_kernel_tss
+  addl $4, %esp
+
   /* Restore the CPU context */
   popw %gs
   popw %fs
@@ -78,7 +102,7 @@
   iret /* equivalent to: popfl ; ret */
 
 resume_pc:
-        // Same context as that when sos_cpu_kstate_switch got called
+        // Same context as that when sos_cpu_context_switch got called
         // arg2= to_context    -- esp+8
         // arg1= from_context  -- esp+4
         // caller ip           -- esp
@@ -87,9 +111,9 @@
 
 
 /* ------------------------- */
-.globl sos_cpu_kstate_exit_to
-.type sos_cpu_kstate_exit_to, @function
-sos_cpu_kstate_exit_to:
+.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
@@ -107,6 +131,14 @@
   call  *8(%eax)
   addl  $4, %esp
 
+  /* Prepare kernel TSS in case we are switching to a user thread: we
+     make sure that we will come back into the kernel at a correct
+     stack location */
+  pushl %esp /* Pass the location of the context we are
+	        restoring to the function */
+  call sos_cpu_context_update_kernel_tss
+  addl $4, %esp
+
   /* Restore the CPU context */
   popw %gs
   popw %fs
@@ -125,4 +157,3 @@
 
   /* This restores the eflags, the cs and the eip registers */
   iret /* equivalent to: popfl ; ret */
-
diff -ruN /tmp/sos-code-article6.75/hwcore/exception.c ../sos-code-article7/hwcore/exception.c
--- /tmp/sos-code-article6.75/hwcore/exception.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/exception.c	2005-04-27 20:14:18.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -19,6 +18,10 @@
 #include "idt.h"
 #include "irq.h"
 
+#include <sos/assert.h>
+#include <drivers/bochs.h>
+#include <sos/thread.h>
+
 #include "exception.h"
 
 /* array of exception wrappers, defined in exception_wrappers.S */
@@ -28,8 +31,85 @@
 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, struct sos_cpu_state *ctxt)
+{
+  const char *exname = sos_exception_get_name(exid);
+
+  if (sos_cpu_context_is_in_user_mode(ctxt))
+    {
+      /* Exception while in user mode */
+      sos_bochs_printf("Exception %s in User mode at instruction 0x%x (info=%x)!\n",
+		       exname,
+		       sos_cpu_context_get_PC(ctxt),
+		       (unsigned)sos_cpu_context_get_EX_info(ctxt));
+      sos_bochs_printf("Terminating User thread\n");
+      sos_thread_exit();
+    }
+  else
+    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
@@ -90,3 +170,12 @@
   /* 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 -ruN /tmp/sos-code-article6.75/hwcore/exception.h ../sos-code-article7/hwcore/exception.h
--- /tmp/sos-code-article6.75/hwcore/exception.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/exception.h	2005-04-27 20:14:18.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -71,12 +70,14 @@
 #ifndef ASM_SOURCE
 
 typedef void (*sos_exception_handler_t)(int exception_number,
-					const struct sos_cpu_kstate *cpu_kstate);
+					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 -ruN /tmp/sos-code-article6.75/hwcore/exception_wrappers.S ../sos-code-article7/hwcore/exception_wrappers.S
--- /tmp/sos-code-article6.75/hwcore/exception_wrappers.S	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/exception_wrappers.S	2005-04-27 20:14:18.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The KOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -17,6 +16,8 @@
    USA. 
 */
 #include "exception.h"
+
+#include "segment.h"
          
 .file "exception_wrappers.S"
 
@@ -29,7 +30,14 @@
    with exception.c */
 .globl sos_exception_wrapper_array
 
-	
+/** Update the kernel TSS in case we are switching to a thread in user
+	mode in order to come back into the correct kernel stack */
+.extern sos_cpu_context_update_kernel_tss
+
+/* The address of the function to call to set back the user thread's
+   MMU configuration upon return to user context */
+.extern sos_thread_prepare_exception_switch_back
+
 /**
  * For exceptions with/without error code, refer to Intel x86 doc vol 3,
  * section 5.12
@@ -86,6 +94,13 @@
                 pushw %fs
                 pushw %gs
  
+		/* Set correct kernel segment descriptors' value */
+		movw $SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA), %di
+		pushw %di ; popw %ds
+		pushw %di ; popw %es
+		pushw %di ; popw %fs
+		pushw %di ; popw %gs
+
 		/*
 		 * Call the handler with the exception number and the
 		 * address of the stored CPU context as arguments
@@ -97,6 +112,19 @@
                 /* Unallocate the arguments passed to the handler */
                 addl  $8, %esp
 
+	        /* Reconfigure the MMU if needed */
+                pushl %esp /* cpu_ctxt */
+                call sos_thread_prepare_exception_switch_back
+                addl  $4, %esp /* Unallocate the stack */
+
+		/* Prepare kernel TSS in case we are switching to a
+                   user thread: we make sure that we will come back
+                   into the kernel at a correct stack location */
+		pushl %esp /* Pass the location of the context we are
+  	                      restoring to the function */
+                call sos_cpu_context_update_kernel_tss
+                addl $4, %esp
+
                 /* Restore the context */
                 popw  %gs
                 popw  %fs
@@ -152,6 +180,13 @@
                 pushw %fs
                 pushw %gs
 
+		/* Set correct kernel segment descriptors' value */
+		movw $SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA), %di
+		pushw %di ; popw %ds
+		pushw %di ; popw %es
+		pushw %di ; popw %fs
+		pushw %di ; popw %gs
+
 		/*
 		 * Call the handler with the exception number and the
 		 * address of the stored CPU context as arguments
@@ -163,6 +198,19 @@
                 /* Unallocate the arguments passed to the handler */
                 addl  $8, %esp
 
+	        /* Reconfigure the MMU if needed */
+                pushl %esp /* cpu_ctxt */
+                call sos_thread_prepare_exception_switch_back
+                addl  $4, %esp /* Unallocate the stack */
+
+		/* Prepare kernel TSS in case we are switching to a
+                   user thread: we make sure that we will come back
+                   into the kernel at a correct stack location */
+		pushl %esp /* Pass the location of the context we are
+  	                      restoring to the function */
+                call sos_cpu_context_update_kernel_tss
+                addl $4, %esp
+
                 /* Restore the context */
                 popw  %gs
                 popw  %fs
diff -ruN /tmp/sos-code-article6.75/hwcore/gdt.c ../sos-code-article7/hwcore/gdt.c
--- /tmp/sos-code-article6.75/hwcore/gdt.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/gdt.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,5 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
+   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
@@ -38,7 +38,7 @@
   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;
+  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 */
@@ -114,6 +114,10 @@
   [SOS_SEG_NULL]  = (struct x86_segment_descriptor){ 0, },
   [SOS_SEG_KCODE] = BUILD_GDTE(0, 1),
   [SOS_SEG_KDATA] = BUILD_GDTE(0, 0),
+  [SOS_SEG_UCODE] = BUILD_GDTE(3, 1),
+  [SOS_SEG_UDATA] = BUILD_GDTE(3, 0),
+  [SOS_SEG_KERNEL_TSS] = { 0, } /* Initialized by
+				   register_kernel_tss */
 };
 
 sos_ret_t sos_gdt_subsystem_setup(void)
@@ -148,3 +152,35 @@
 
   return SOS_OK;
 }
+
+
+sos_ret_t sos_gdt_register_kernel_tss(sos_vaddr_t tss_vaddr)
+{
+  sos_ui16_t regval_tss;
+
+  /* Initialize the GDT entry */
+  gdt[SOS_SEG_KERNEL_TSS]
+    = (struct x86_segment_descriptor) {
+      .limit_15_0=            0x67, /* See Intel x86 vol 3 section 6.2.2 */
+      .base_paged_addr_15_0=  (tss_vaddr) & 0xffff,
+      .base_paged_addr_23_16= (tss_vaddr >> 16) & 0xff,
+      .segment_type=          0x9,  /* See Intel x86 vol 3 figure 6-3 */
+      .descriptor_type=       0,    /* (idem) */
+      .dpl=                   3,    /* Allowed for CPL3 tasks */
+      .present=               1,
+      .limit_19_16=           0,    /* Size of a TSS is < 2^16 ! */
+      .custom=                0,    /* Unused */
+      .op_size=               0,    /* See Intel x86 vol 3 figure 6-3 */
+      .granularity=           1,    /* limit is in Bytes */
+      .base_paged_addr_31_24= (tss_vaddr >> 24) & 0xff
+    };
+
+  /* Load the TSS register into the processor */
+  regval_tss = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE,
+					   SOS_SEG_KERNEL_TSS);
+  asm ("ltr %0" : :"r"(regval_tss));
+  
+  return SOS_OK;     
+}
+
+
diff -ruN /tmp/sos-code-article6.75/hwcore/gdt.h ../sos-code-article7/hwcore/gdt.h
--- /tmp/sos-code-article6.75/hwcore/gdt.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/gdt.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -30,6 +29,7 @@
  * @see Intel x86 doc vol 3, chapter 3
  */
 
+#include <sos/types.h>
 #include <sos/errno.h>
 
 /**
@@ -38,4 +38,15 @@
  */
 sos_ret_t sos_gdt_subsystem_setup(void);
 
+
+/**
+ * Register the given address as the kernel TSS (for cpl3 -> cpl0
+ * privilege changes). Assumes that this address corresponds to a
+ * valid TSS.
+ *
+ * @note Internal function called only by the cpu_context subsystem.
+ */
+sos_ret_t sos_gdt_register_kernel_tss(sos_vaddr_t tss_vaddr);
+
+
 #endif /* _SOS_GDT_H_ */
diff -ruN /tmp/sos-code-article6.75/hwcore/i8254.c ../sos-code-article7/hwcore/i8254.c
--- /tmp/sos-code-article6.75/hwcore/i8254.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/i8254.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The KOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/i8254.h ../sos-code-article7/hwcore/i8254.h
--- /tmp/sos-code-article6.75/hwcore/i8254.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/i8254.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/i8259.c ../sos-code-article7/hwcore/i8259.c
--- /tmp/sos-code-article6.75/hwcore/i8259.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/i8259.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The KOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/i8259.h ../sos-code-article7/hwcore/i8259.h
--- /tmp/sos-code-article6.75/hwcore/i8259.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/i8259.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/idt.c ../sos-code-article7/hwcore/idt.c
--- /tmp/sos-code-article6.75/hwcore/idt.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/idt.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/idt.h ../sos-code-article7/hwcore/idt.h
--- /tmp/sos-code-article6.75/hwcore/idt.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/idt.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/ioports.h ../sos-code-article7/hwcore/ioports.h
--- /tmp/sos-code-article6.75/hwcore/ioports.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/ioports.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  All GPL'ed OS
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/irq.c ../sos-code-article7/hwcore/irq.c
--- /tmp/sos-code-article6.75/hwcore/irq.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/irq.c	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article6.75/hwcore/irq.h ../sos-code-article7/hwcore/irq.h
--- /tmp/sos-code-article6.75/hwcore/irq.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/irq.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  David Decotigny
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -63,8 +62,7 @@
 
 
 /** Definition of an hardware IRQ handler */
-typedef void (*sos_irq_handler_t)(int irq_level,
-				  const struct sos_cpu_kstate *cpu_kstate);
+typedef void (*sos_irq_handler_t)(int irq_level);
 
 
 /** Setup the PIC */
diff -ruN /tmp/sos-code-article6.75/hwcore/irq_wrappers.S ../sos-code-article7/hwcore/irq_wrappers.S
--- /tmp/sos-code-article6.75/hwcore/irq_wrappers.S	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/irq_wrappers.S	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The KOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -16,7 +15,8 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    USA. 
 */
-#define ASM_SOURCE 1
+
+#include "segment.h"
          
 .file "irq_wrappers.S"
 
@@ -32,6 +32,25 @@
 /** The variable holding the nested level of the IRQ handlers */
 .extern sos_irq_nested_level_counter
 
+/** Update the interrupted current thread's CPU context
+    Its prototype is:
+      sos_thread_prepare_irq_servicing(struct sos_cpu_state *);
+*/
+.extern sos_thread_prepare_irq_servicing
+
+/** Update the kernel TSS in case we are switching to a thread in user
+	mode in order to come back into the correct kernel stack */
+.extern sos_cpu_context_update_kernel_tss
+
+/** Select a thread to set on CPU (this enables user-threads
+    preemption) and configure the MMU to match that of the destination
+    thread.
+    Its prototype is:
+      struct sos_cpu_state * // Selected CPU context
+      sos_thread_prepare_irq_switch_back();
+*/
+.extern sos_thread_prepare_irq_switch_back
+
 /* These pre-handlers are for IRQ (Master PIC) */
 .irp id, 0,1,2,3,4,5,6,7
 
@@ -64,10 +83,26 @@
 		pushw %fs
 		pushw %gs
 
+		/* Set correct kernel segment descriptors' value */
+		movw $SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA), %di
+		pushw %di ; popw %ds
+		pushw %di ; popw %es
+		pushw %di ; popw %fs
+		pushw %di ; popw %gs
+
 		/*
 		 * Increment IRQ nested level
 		 */
 		incl sos_irq_nested_level_counter
+		/* Outermost IRQ only: store the interrupted context
+		   of the current thread */
+		cmpl $1, sos_irq_nested_level_counter
+		jne 1f
+		pushl %esp
+		call sos_thread_prepare_irq_servicing
+		addl $4, %esp
+
+        1:
 
 		/* Send EOI to PIC. See Intel 8259 datasheet
 		   available on Kos website */	
@@ -75,15 +110,12 @@
 		outb  %al, $0x20
 	
 		/*
-		 * Call the handler with the IRQ number and the
-		 * address of the stored CPU context as arguments
+		 * Call the handler with IRQ number as argument
 		 */
-		pushl %esp
 		pushl $\id
 		leal  sos_irq_handler_array,%edi
 		call  *\id*4(%edi)
-                /* Unallocate the arguments passed to the handler */
-		addl  $8, %esp
+		addl  $4, %esp
 	
 		/*
 		 * Decrement IRQ nested level
@@ -102,6 +134,22 @@
 
 	2:	/* No:	 all right ! */
 
+		/* Was this the outermost IRQ handler ? */
+		jnz 3f
+
+		/* Yes:	reschedule */
+		call sos_thread_prepare_irq_switch_back
+		/* Establish new context: context switch ! */
+		movl %eax, %esp
+
+		/* Prepare kernel TSS in case we are switching to a
+                   user thread: we make sure that we will come back
+                   into the kernel at a correct stack location */
+		pushl %esp /* Pass the location of the context we are
+  	                      restoring to the function */
+                call sos_cpu_context_update_kernel_tss
+                addl $4, %esp
+	3:
 		/* Restore the context */
 		popw  %gs
 		popw  %fs
@@ -156,10 +204,26 @@
 		pushw %fs
 		pushw %gs
 
+		/* Set correct kernel segment descriptors' value */
+		movw $SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA), %di
+		pushw %di ; popw %ds
+		pushw %di ; popw %es
+		pushw %di ; popw %fs
+		pushw %di ; popw %gs
+
 		/*
 		 * Increment IRQ nested level
 		 */
 		incl sos_irq_nested_level_counter
+		/* Outermost IRQ only: store the interrupted context
+		   of the current thread */
+		cmpl $1, sos_irq_nested_level_counter
+		jne 1f
+		pushl %esp
+		call sos_thread_prepare_irq_servicing
+		addl $4, %esp
+
+        1:
 
 		/* Send EOI to PIC. See Intel 8259 datasheet
 		   available on Kos website */	
@@ -168,15 +232,12 @@
 		outb  %al, $0x20
 
 		/*
-		 * Call the handler with the IRQ number and the
-		 * address of the stored CPU context as arguments
+		 * Call the handler with IRQ number as argument
 		 */
-		pushl %esp
 		pushl $\id
 		leal  sos_irq_handler_array,%edi
 		call  *\id*4(%edi)
-                /* Unallocate the arguments passed to the handler */
-		addl  $8, %esp
+		addl  $4, %esp
 
 		/*
 		 * Decrement IRQ nested level
@@ -195,6 +256,22 @@
 
 	2:	/* No:	 all right ! */
 
+		/* Was this the outermost IRQ handler ? */
+		jnz 3f
+
+		/* Yes:	reschedule */
+		call sos_thread_prepare_irq_switch_back
+		/* Establish new context: context switch ! */
+		movl %eax, %esp
+
+		/* Prepare kernel TSS in case we are switching to a
+                   user thread: we make sure that we will come back
+                   into the kernel at a correct stack location */
+		pushl %esp /* Pass the location of the context we are
+  	                      restoring to the function */
+                call sos_cpu_context_update_kernel_tss
+                addl $4, %esp
+	3:
 		/* Restore the context */
 		popw  %gs
 		popw  %fs
diff -ruN /tmp/sos-code-article6.75/hwcore/mm_context.c ../sos-code-article7/hwcore/mm_context.c
--- /tmp/sos-code-article6.75/hwcore/mm_context.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/hwcore/mm_context.c	2005-04-27 20:14:19.000000000 +0200
@@ -0,0 +1,297 @@
+/* Copyright (C) 2005  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 <hwcore/paging.h>
+#include <hwcore/irq.h>
+
+#include <sos/assert.h>
+#include <sos/list.h>
+#include <sos/klibc.h>
+#include <sos/physmem.h>
+#include <sos/kmem_slab.h>
+#include <sos/kmem_vmm.h>
+
+#include "mm_context.h"
+
+
+/**
+ * Definition of an MMU context.
+ */
+struct sos_mm_context
+{
+  /** Physical address of the PD for this MMU context */
+  sos_paddr_t paddr_PD;
+
+  /** Virtual address where it is mapped into the Kernel space */
+  sos_vaddr_t vaddr_PD;
+
+  /** Reference counter for this mm_context */
+  sos_ui32_t  ref_cnt;
+
+  /** List of MMU contexts in the system */
+  struct sos_mm_context *prev, *next;
+};
+
+
+/**
+ * The cache of mm_context structures
+ */
+struct sos_kslab_cache * cache_struct_mm_context;
+
+
+/**
+ * The current MMU context corresponding to the current configuration
+ * of the MMU.
+ */
+static struct sos_mm_context *current_mm_context = NULL;
+
+
+/**
+ * System-wide list of all the mm_contexts in the system
+ */
+static struct sos_mm_context *list_mm_context = NULL;
+/* The "= NULL" here is FUNDAMENTAL, because paging.c must work
+   correctly, ie synch_PDE below must behave reasonably (eg do
+   nothing), until the mm_context subsystem has been initialized. */
+
+
+sos_ret_t sos_mm_context_subsystem_setup()
+{
+  struct sos_mm_context * initial_mm_context;
+  sos_ret_t retval;
+
+  /* Create the new mm_context cache */
+  cache_struct_mm_context = sos_kmem_cache_create("struct mm_context",
+						  sizeof(struct sos_mm_context),
+						  1, 0,
+						  SOS_KSLAB_CREATE_MAP);
+  if (NULL == cache_struct_mm_context)
+    return -SOS_ENOMEM;
+
+  /*
+   * Allocate the initial mm_context structure
+   */
+  initial_mm_context
+    = (struct sos_mm_context*) sos_kmem_cache_alloc(cache_struct_mm_context,
+						    SOS_KSLAB_ALLOC_ATOMIC);
+  if (NULL == initial_mm_context)
+    return -SOS_ENOMEM;
+
+  /* Retrieve the address of the current page where the PD lies */
+  initial_mm_context->paddr_PD = sos_paging_get_current_PD_paddr();
+
+  /*
+   * Map it somewhere in kernel virtual memory
+   */
+
+  /* Allocate 1 page of kernel Virtual memory */
+  initial_mm_context->vaddr_PD = sos_kmem_vmm_alloc(1, 0);
+  if (initial_mm_context->vaddr_PD == 0)
+    return -SOS_ENOMEM;
+
+  /* Map the PD at this virtual address: it will thus be mapped 2
+     times (1 time for the mirroring, 1 time for mm_context) ! */
+  retval = sos_paging_map(initial_mm_context->paddr_PD,
+			  initial_mm_context->vaddr_PD,
+			  FALSE,
+			  SOS_VM_MAP_PROT_READ
+			  | SOS_VM_MAP_PROT_WRITE);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Initialize the initial list of mm_contexts */
+  list_singleton(list_mm_context, initial_mm_context);
+
+  /* We just created this mm_context: mark it as "referenced" */
+  initial_mm_context->ref_cnt ++;
+
+  /* We are actually already using it ! */
+  initial_mm_context->ref_cnt ++; /* ie reference it a 2nd time ! */
+  current_mm_context = initial_mm_context;
+
+  return SOS_OK;
+}
+
+
+struct sos_mm_context * sos_mm_context_create(void)
+{
+  sos_ui32_t flags;
+  struct sos_mm_context *mmctxt;
+
+  /*
+   * Allocate the initial mm_context structure
+   */
+  mmctxt = (struct sos_mm_context*) sos_kmem_cache_alloc(cache_struct_mm_context, 0);
+  if (NULL == mmctxt)
+    return NULL;
+
+  /* Allocate a new page for the new PD and map it into the kernel */
+  mmctxt->vaddr_PD = sos_kmem_vmm_alloc(1, SOS_KMEM_VMM_MAP);
+  if (mmctxt->vaddr_PD == 0)
+    {
+      sos_kmem_cache_free((sos_vaddr_t) mmctxt);
+      return NULL;
+    }
+
+  /* Retrieve its physical address */
+  mmctxt->paddr_PD = sos_paging_get_paddr(mmctxt->vaddr_PD);
+  if (mmctxt->paddr_PD == 0)
+    {
+      sos_kmem_cache_free((sos_vaddr_t) mmctxt);
+      return NULL;
+    }
+
+  /* Copy the current hardware MMU address translation tables */
+  if (SOS_OK != sos_paging_copy_kernel_space(mmctxt->vaddr_PD,
+					     current_mm_context->vaddr_PD))
+    {
+      sos_kmem_cache_free((sos_vaddr_t) mmctxt);
+      return NULL;
+    }
+
+  /* Mark the mm_context as "referenced" */
+  mmctxt->ref_cnt = 1;
+
+  /* Add it to the list of MMU contexts */
+  sos_disable_IRQs(flags);
+  list_add_tail(list_mm_context, mmctxt);
+  sos_restore_IRQs(flags);
+
+  return mmctxt;
+}
+
+
+sos_ret_t sos_mm_context_unref(struct sos_mm_context *mmctxt)
+{
+  sos_ui32_t flags;
+
+  sos_disable_IRQs(flags);
+
+  /* A valid mmctxt is one which is not yet unreferenced */
+  SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
+
+  /* Unreference it */
+  mmctxt->ref_cnt --;
+
+  /* If somebody is still using it, don't release it now */
+  if (mmctxt->ref_cnt > 0)
+    {
+      sos_restore_IRQs(flags);
+      return SOS_OK;
+    }
+
+  /* If nobody uses it, then it cannot be the current mm_context ! */
+  SOS_ASSERT_FATAL(mmctxt != current_mm_context);
+
+  /* Remove it from the list of mm_contexts */
+  list_delete(list_mm_context, mmctxt);
+
+  sos_restore_IRQs(flags);
+
+  /* Remove all user mappings (if any) */
+  sos_paging_dispose(mmctxt->vaddr_PD);
+
+  /* Unmap the PD from the kernel */
+  sos_kmem_vmm_free(mmctxt->vaddr_PD);
+
+  memset(mmctxt, 0x0, sizeof(*mmctxt));
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_mm_context_ref(struct sos_mm_context *mmctxt)
+{
+  sos_ui32_t flags;
+
+  sos_disable_IRQs(flags);
+
+  /* A valid mmctxt is one which is not yet unreferenced */
+  SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
+
+  /* Reference it once again */
+  mmctxt->ref_cnt ++;
+
+  sos_restore_IRQs(flags);
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_mm_context_switch_to(struct sos_mm_context *mmctxt)
+{
+  SOS_ASSERT_FATAL(NULL != mmctxt);
+  SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
+  SOS_ASSERT_FATAL(current_mm_context->ref_cnt > 0);
+  if (mmctxt != current_mm_context)
+    {
+      sos_ui32_t flags;
+      struct sos_mm_context * prev_mm_context = current_mm_context;
+
+      /* This is the most dangerous part of the whole thing. If we set
+	 the wrong MMU configuration in mmctxt, this will hang or
+	 reboot the machine... */
+      sos_paging_set_current_PD_paddr(mmctxt->paddr_PD);
+
+      /* Exchange the mm_contexts */
+      current_mm_context = mmctxt;
+
+      /* Update the reference counts */
+      sos_disable_IRQs(flags);
+      mmctxt->ref_cnt ++;
+      sos_mm_context_unref(prev_mm_context);
+      sos_restore_IRQs(flags);
+    }
+
+  return SOS_OK;
+}
+
+
+struct sos_mm_context *get_current_mm_context()
+{
+  SOS_ASSERT_FATAL(current_mm_context->ref_cnt > 0);
+  return current_mm_context;
+}
+
+
+/* ******************************************************
+ * Reserved functions
+ */
+
+
+sos_ret_t sos_mm_context_synch_kernel_PDE(unsigned int index_in_pd,
+					  sos_ui32_t pde)
+{
+  sos_ui32_t flags;
+  struct sos_mm_context * dest_mm_context;
+  int nb_mm_contexts;
+
+  sos_disable_IRQs(flags);
+  list_foreach_forward(list_mm_context, dest_mm_context, nb_mm_contexts)
+    {
+      sos_ui32_t * dest_pd;
+
+      SOS_ASSERT_FATAL(dest_mm_context->ref_cnt > 0);
+
+      dest_pd = (sos_ui32_t*) dest_mm_context->vaddr_PD;
+      dest_pd[index_in_pd] = pde;
+    }
+  sos_restore_IRQs(flags);
+
+  return SOS_OK;
+}
diff -ruN /tmp/sos-code-article6.75/hwcore/mm_context.h ../sos-code-article7/hwcore/mm_context.h
--- /tmp/sos-code-article6.75/hwcore/mm_context.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/hwcore/mm_context.h	2005-04-27 20:14:19.000000000 +0200
@@ -0,0 +1,101 @@
+/* Copyright (C) 2005  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_MMCTXT_H_
+#define _SOS_MMCTXT_H_
+
+
+/**
+ * @file mm_context.h
+ *
+ * Low level API to manage multiple MMU translation tables. The API
+ * (not its implementation) should be some kind of
+ * architecture-independent.
+ *
+ * The goal of this API is:
+ *   - To provide a simple arch-independent API to change the current
+ *     address space configured in the MMU
+ *   - To make sure that ALL the kernel space mappings are always kept
+ *     IDENTICAL among all the address spaces in the whole system. That
+ *     way, a virtual address in the kernel space refers to EXACTLY the
+ *     same physical memory location for all the address spaces.
+ */
+
+#include <sos/types.h>
+#include <sos/errno.h>
+
+
+/**
+ * Declaration of an MMU context. Opaque structure defined in
+ * mm_context.c
+ */
+struct sos_mm_context;
+
+
+/**
+ * Setup the MMU context management subsystem
+ */
+sos_ret_t sos_mm_context_subsystem_setup();
+
+
+/**
+ * Public function to allocate a new MMU context.
+ *
+ * Allocate a new PD, map it into kernel virtual address space and
+ * initialize its PDE for the Kernel space so that the Kernel space is
+ * kept identical between ALL the MMU context in the system.
+ */
+struct sos_mm_context * sos_mm_context_create(void);
+
+
+/**
+ * Public function to release the given MMU context (based on
+ * reference counting). Only the virtual page that maps the PD of the
+ * MMU context is released. All the other user-space pages are
+ * expected to be already released by the parent process.
+ */
+sos_ret_t sos_mm_context_unref(struct sos_mm_context *mmctxt);
+
+
+/**
+ * Reconfigure the MMU to use a different MMU context. mmctxt MUST
+ * NOT be NULL.
+ */
+sos_ret_t sos_mm_context_switch_to(struct sos_mm_context *mmctxt);
+
+
+/* ******************************************************
+ * Reserved functions
+ */
+
+
+/**
+ * Reserved function to increase the reference count of the given MMU
+ * context (based on reference counting).
+ */
+sos_ret_t sos_mm_context_ref(struct sos_mm_context *mmctxt);
+
+
+/**
+ * Restricted function reserved to paging.c to synchronize all the PDEs
+ * accross all the MMU contexts in the system. This is limited to PDEs
+ * pertaining to the kernel space.
+ */
+sos_ret_t sos_mm_context_synch_kernel_PDE(unsigned int index_in_pd,
+					  sos_ui32_t pde);
+
+#endif /* _SOS_MMCTXT_H_ */
diff -ruN /tmp/sos-code-article6.75/hwcore/paging.c ../sos-code-article7/hwcore/paging.c
--- /tmp/sos-code-article6.75/hwcore/paging.c	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/paging.c	2005-04-27 20:14:18.000000000 +0200
@@ -19,8 +19,29 @@
 #include <sos/klibc.h>
 #include <sos/assert.h>
 
+#include "mm_context.h"
+
 #include "paging.h"
 
+
+/*
+ * Important NOTICE concerning the use of the reference & occupation
+ * counters of the physical pages by the "paging" subsystem:
+ *   - All the kernel PT are SHARED. This means that as soon as one
+ *     kernel PT belongs to one mm_context, it belongs to ALL the
+ *     mm_contexts. We don't update the real reference count of the PT
+ *     in this respect, because it would require to update the
+ *     reference counts of ALL the kernel PTs as soon as a new
+ *     mm_context is created, or as soon as a mm_context is
+ *     suppressed. This way, the reference count is constant
+ *     independently of the actual number of PD really sharing them.
+ *   - We do NOT maintain the occupation count of the PDs. This would add
+ *     some little overhead that is useless
+ *   - We do maintain the occupation count of ALL the PTs: it represents the
+ *     number of PTE allocated in the PT
+ */
+
+
 /** The structure of a page directory entry. See Intel vol 3 section
     3.6.4 */
 struct x86_pde
@@ -39,6 +60,13 @@
 } __attribute__ ((packed));
 
 
+/** Intermediate type to speed up PDE copy */
+typedef union {
+  struct x86_pde pde;
+  sos_ui32_t     ui32;
+} x86_pde_val_t;
+
+
 /** The structure of a page table entry. See Intel vol 3 section
     3.6.4 */
 struct x86_pte
@@ -58,6 +86,13 @@
 } __attribute__ ((packed));
 
 
+/** Intermediate type to speed up PTE copy */
+typedef union {
+  struct x86_pte pte;
+  sos_ui32_t     ui32;
+} x86_pte_val_t;
+
+
 /** Structure of the x86 CR3 register: the Page Directory Base
     Register. See Intel x86 doc Vol 3 section 2.5 */
 struct x86_pdbr
@@ -137,17 +172,11 @@
     {
       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
+      /* 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_ASSERT_FATAL(FALSE); /* indicate a fatal error */
     }
   else
@@ -178,6 +207,10 @@
   pt[index_in_pt].user    = 0;
   pt[index_in_pt].paddr   = ppage >> 12;
 
+  /* Increase the PT's occupation count because we allocated a new PTE
+     inside it */
+  sos_physmem_inc_physpage_occupation((sos_paddr_t)pt);
+
   return SOS_OK;
 }
 
@@ -278,6 +311,12 @@
   struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR
 					   + SOS_PAGE_SIZE*index_in_pd);
 
+  SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(ppage_paddr));
+  SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(vpage_vaddr));
+
+  /* EXEC permission ignored on x86 */
+  flags &= ~SOS_VM_MAP_PROT_EXEC;
+
   /* 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))
@@ -286,6 +325,8 @@
   /* Map a page for the PT if necessary */
   if (! pd[index_in_pd].present)
     {
+      x86_pde_val_t u;
+      
       /* No : allocate a new one */
       sos_paddr_t pt_ppage
 	= sos_physmem_ref_physpage_new(! (flags & SOS_VM_MAP_ATOMIC));
@@ -294,11 +335,41 @@
 	  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;
+      /* Prepare the value of the PDE */
+      u.pde = (struct x86_pde){
+	.present  = TRUE,
+	.write    = 1,
+	.pt_paddr = ((sos_paddr_t)pt_ppage) >> 12
+      };
+
+      /* Is it a PDE concerning the kernel space */
+      if (vpage_vaddr < SOS_PAGING_MIRROR_VADDR)
+	{
+	  /* Yes: So we need to update the PDE of ALL the mm_contexts
+	     in the system */
+
+	  /* First of all: this is a kernel PT */
+	  u.pde.user = 0;
+
+	  /* Now synchronize all the PD */
+	  SOS_ASSERT_FATAL(SOS_OK ==
+			   sos_mm_context_synch_kernel_PDE(index_in_pd,
+							   u.ui32));
+	}
+      else /* We should have written "else if (vpage_vaddr >=
+	      SOS_PAGING_BASE_USER_ADDRESS)" but this is not needed
+	      because the beginning of the function detects and
+	      rejects mapping requests inside the mirroring */
+	{
+	  /* No: The request concerns the user space. So only the
+	     current MMU context is concerned */
+
+	  /* First of all: this is a user PT */
+	  u.pde.user = 1;
+
+	  /* Now update the current PD */
+	  pd[index_in_pd] = u.pde;
+	}
       
       /*
        * The PT is now mapped in the PD mirroring
@@ -311,10 +382,10 @@
       memset((void*)pt, 0x0, SOS_PAGE_SIZE);
     }
 
-  /* If we allocate a new entry in the PT, increase its reference
+  /* If we allocate a new entry in the PT, increase its occupation
      count. */
-  else if (! pt[index_in_pt].present)
-    sos_physmem_ref_physpage_at(pd[index_in_pd].pt_paddr << 12);
+  if (! pt[index_in_pt].present)
+    sos_physmem_inc_physpage_occupation(pd[index_in_pd].pt_paddr << 12);
   
   /* Otherwise, that means that a physical page is implicitely
      unmapped */
@@ -328,6 +399,7 @@
   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
    */
@@ -341,7 +413,7 @@
 
 sos_ret_t sos_paging_unmap(sos_vaddr_t vpage_vaddr)
 {
-  sos_ret_t pt_unref_retval;
+  sos_ret_t pt_dec_occupation_retval;
 
   /* Get the page directory entry and table entry index for this
      address */
@@ -357,6 +429,8 @@
   struct x86_pte * pt = (struct x86_pte*) (SOS_PAGING_MIRROR_VADDR
 					   + SOS_PAGE_SIZE*index_in_pd);
 
+  SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(vpage_vaddr));
+
   /* No page mapped at this address ? */
   if (! pd[index_in_pd].present)
     return -SOS_EINVAL;
@@ -378,13 +452,45 @@
   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 > 0)
+  pt_dec_occupation_retval
+    = sos_physmem_dec_physpage_occupation(pd[index_in_pd].pt_paddr << 12);
+  SOS_ASSERT_FATAL(pt_dec_occupation_retval >= 0);
+  if (pt_dec_occupation_retval > 0)
     /* If the PT is now completely unused... */
     {
-      /* Release the PDE */
-      memset(pd + index_in_pd, 0x0, sizeof(struct x86_pde));
+      x86_pde_val_t u;
+
+
+      /*
+       * The PT is not referenced by this PD anymore
+       */
+      sos_physmem_unref_physpage(pd[index_in_pd].pt_paddr << 12);
+
+
+      /*
+       * Reset the PDE
+       */
+
+      /* Mark the PDE as unavailable */
+      u.ui32 = 0;
+
+      /* Is it a PDE concerning the kernel space */
+      if (vpage_vaddr < SOS_PAGING_MIRROR_VADDR)
+	{
+	  /* Now synchronize all the PD */
+	  SOS_ASSERT_FATAL(SOS_OK ==
+			   sos_mm_context_synch_kernel_PDE(index_in_pd,
+							   u.ui32));
+	}
+      else /* We should have written "else if (vpage_vaddr >=
+	      SOS_PAGING_BASE_USER_ADDRESS)" but this is not needed
+	      because the beginning of the function detects and
+	      rejects mapping requests inside the mirroring */
+	{
+	  /* No: The request concerns the user space. So only the
+	     current MMU context is concerned */
+	  pd[index_in_pd] = u.pde;
+	}
       
       /* Update the TLB */
       invlpg(pt);
@@ -394,9 +500,29 @@
 }
 
 
-int sos_paging_get_prot(sos_vaddr_t vaddr)
+sos_ret_t sos_paging_unmap_interval(sos_vaddr_t vaddr,
+				    sos_size_t  size)
 {
-  int retval;
+  sos_ret_t retval = 0;
+
+  if (! SOS_IS_PAGE_ALIGNED(vaddr))
+    return -SOS_EINVAL;
+  if (! SOS_IS_PAGE_ALIGNED(size))
+    return -SOS_EINVAL;
+
+  for ( ;
+	size >= SOS_PAGE_SIZE ;
+	vaddr += SOS_PAGE_SIZE, size -= SOS_PAGE_SIZE)
+    if (SOS_OK == sos_paging_unmap(vaddr))
+      retval += SOS_PAGE_SIZE;
+
+  return retval;
+}
+
+
+sos_ui32_t sos_paging_get_prot(sos_vaddr_t vaddr)
+{
+  sos_ui32_t retval;
 
   /* Get the page directory entry and table entry index for this
      address */
@@ -427,6 +553,63 @@
 }
 
 
+sos_ret_t sos_paging_set_prot(sos_vaddr_t vaddr,
+			      sos_ui32_t  new_prot)
+{
+  /* 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);
+
+  /* EXEC permission ignored on x86 */
+  new_prot &= ~SOS_VM_MAP_PROT_EXEC;
+
+  /* Check flags */
+  if (new_prot & ~(SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE))
+    return -SOS_EINVAL;
+  if (! (new_prot & SOS_VM_MAP_PROT_READ))
+    /* x86 READ flag always set by default */
+    return -SOS_ENOSUP;
+
+  /* No page mapped at this address ? */
+  if (! pd[index_in_pd].present)
+    return -SOS_EINVAL;
+  if (! pt[index_in_pt].present)
+    return -SOS_EINVAL;
+
+  /* Update access rights */
+  pt[index_in_pt].write = ((new_prot & SOS_VM_MAP_PROT_WRITE) != 0);
+  invlpg(vaddr);
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_paging_set_prot_of_interval(sos_vaddr_t vaddr,
+					  sos_size_t  size,
+					  sos_ui32_t  new_prot)
+{
+  if (! SOS_IS_PAGE_ALIGNED(vaddr))
+    return -SOS_EINVAL;
+  if (! SOS_IS_PAGE_ALIGNED(size))
+    return -SOS_EINVAL;
+
+  for ( ; size >= SOS_PAGE_SIZE ; vaddr += SOS_PAGE_SIZE, size -= SOS_PAGE_SIZE)
+    sos_paging_set_prot(vaddr, new_prot);
+
+  return SOS_OK;
+}
+
+
 sos_paddr_t sos_paging_get_paddr(sos_vaddr_t vaddr)
 {
   /* Get the page directory entry and table entry index for this
@@ -453,3 +636,154 @@
   return (pt[index_in_pt].paddr << 12) + offset_in_page;
 }
 
+
+/* *************************************************
+ * Functions restricted to mm_context module
+ */
+
+
+sos_paddr_t sos_paging_get_current_PD_paddr()
+{
+  struct x86_pdbr pdbr;
+  asm volatile("movl %%cr3, %0\n": "=r"(pdbr));
+  return (pdbr.pd_paddr << 12);
+}
+
+
+sos_ret_t sos_paging_set_current_PD_paddr(sos_paddr_t paddr_PD)
+{
+  struct x86_pdbr pdbr;
+
+  SOS_ASSERT_FATAL(paddr_PD != 0);
+  SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(paddr_PD));
+
+  /* Setup the value of the PDBR */
+  memset(& pdbr, 0x0, sizeof(struct x86_pdbr)); /* Reset the PDBR */
+  pdbr.pd_paddr = (paddr_PD >> 12);
+
+  /* Configure the MMU according to the PDBR */
+  asm volatile ("movl %0,%%cr3\n" ::"r"(pdbr));
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_paging_dispose(sos_vaddr_t vaddr_PD)
+{
+  x86_pde_val_t *pd = (x86_pde_val_t*) vaddr_PD;
+  x86_pte_val_t *pt;
+  int           index_in_pd;
+
+  /* Allocate 1 page in kernel space to map the PTs in order to
+     unreference the physical pages they reference */
+  pt = (x86_pte_val_t *)sos_kmem_vmm_alloc(1, 0);
+  if (! pt)
+    return -SOS_ENOMEM;
+
+  /* (Nothing to do in kernel space) */
+
+  /* Reset all the PTs in user space */
+  for (index_in_pd = (SOS_PAGING_BASE_USER_ADDRESS >> 22) ;
+       index_in_pd < 1024 ; /* 1 PDE = 1 PT
+			       = 1024 Pages
+			       = 4MB */
+       index_in_pd ++)
+    {
+      sos_paddr_t paddr_pt = (pd[index_in_pd].pde.pt_paddr << 12);
+      int index_in_pt;
+
+      /* Nothing to do if there is no PT */
+      if (! pd[index_in_pd].pde.present)
+	{
+	  pd[index_in_pd].ui32 = 0;
+	  continue;
+	}
+
+      /* Map this PT inside kernel */
+      SOS_ASSERT_FATAL(SOS_OK
+		       == sos_paging_map(paddr_pt,
+					 (sos_vaddr_t)pt, FALSE,
+					 SOS_VM_MAP_PROT_READ
+					 | SOS_VM_MAP_PROT_WRITE));
+      
+      /* Reset all the mappings in this PT */
+      for (index_in_pt = 0 ; index_in_pt < 1024 ; index_in_pt ++)
+	{
+	  /* Ignore unmapped PTE */
+	  if (! pt[index_in_pt].pte.present)
+	    {
+	      pt[index_in_pt].ui32 = 0;
+	      continue;
+	    }
+
+	  /* Unreference the associated page */
+	  sos_physmem_unref_physpage(pt[index_in_pt].pte.paddr << 12);
+
+	  /* Decrease occupation count of the PT */
+	  sos_physmem_dec_physpage_occupation(paddr_pt);
+
+	  /* Reset PTE */
+	  pt[index_in_pt].ui32 = 0;
+	}
+
+      /* Unmap PT */
+      SOS_ASSERT_FATAL(SOS_OK == sos_paging_unmap((sos_vaddr_t)pt));
+
+      /* Reset PDE */
+      pd[index_in_pd].ui32 = 0;
+
+      /* Unreference PT */
+      sos_physmem_unref_physpage(paddr_pt);
+    }
+
+  /* Unallocate kernel space used for the temporary PT */
+  SOS_ASSERT_FATAL(SOS_OK == sos_kmem_vmm_free((sos_vaddr_t)pt));
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_paging_copy_kernel_space(sos_vaddr_t dest_vaddr_PD,
+				       sos_vaddr_t src_vaddr_PD)
+{
+  x86_pde_val_t *src_pd       = (x86_pde_val_t*) src_vaddr_PD;
+  x86_pde_val_t *dest_pd      = (x86_pde_val_t*) dest_vaddr_PD;
+  sos_paddr_t   dest_paddr_PD = sos_paging_get_paddr(dest_vaddr_PD);
+  x86_pde_val_t mirror_pde;
+  int           index_in_pd;
+
+  /* Fill destination PD with zeros */
+  memset((void*)dest_vaddr_PD, 0x0, SOS_PAGE_SIZE);
+
+  /* Synchronize it with the master Kernel MMU context. Stop just
+     before the mirroring ! */
+  for (index_in_pd = 0 ;
+       index_in_pd < (SOS_PAGING_MIRROR_VADDR >> 22) ; /* 1 PDE = 1 PT
+							  = 1024 Pages
+							  = 4MB */
+       index_in_pd ++)
+    {
+      /* Copy the master's configuration */
+      dest_pd[index_in_pd].ui32 = src_pd[index_in_pd].ui32;
+
+      /* We DON'T mark the underlying PT and pages as referenced
+	 because all the PD are equivalent in the kernel space: as
+	 soon as a page is mapped in the kernel, it is mapped by X
+	 address spaces, and as soon as it is unmapped by 1 address
+	 space, it is unmapped in all the others. So that for X
+	 address spaces, the reference counter will be either 0 or X,
+	 and not something else: using the reference counter correctly
+	 won't be of any use and would consume some time in updating it. */
+    }
+
+  /* Setup the mirroring for the new address space */
+  mirror_pde.ui32 = 0;
+  mirror_pde.pde.present  = TRUE;
+  mirror_pde.pde.write    = 1;
+  mirror_pde.pde.user     = 0; /* This is a KERNEL PDE */
+  mirror_pde.pde.pt_paddr = (dest_paddr_PD >> 12);
+  dest_pd[SOS_PAGING_MIRROR_VADDR >> 22].ui32 = mirror_pde.ui32;
+
+  return SOS_OK;
+}
+
diff -ruN /tmp/sos-code-article6.75/hwcore/paging.h ../sos-code-article7/hwcore/paging.h
--- /tmp/sos-code-article6.75/hwcore/paging.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/paging.h	2005-04-27 20:14:19.000000000 +0200
@@ -37,6 +37,24 @@
 #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 (must be 4MB-aligned) */
+#define SOS_PAGING_TOP_USER_ADDRESS  (0xFFFFFFFF) /* 4GB - 1B */
+#define SOS_PAGING_USER_SPACE_SIZE   (0xc0000000) /* 3GB */
+
+/** 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
  */
@@ -44,16 +62,12 @@
 #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 */
+#define SOS_VM_MAP_PROT_EXEC  (1<<2) /* Not supported on IA32 */
+
 /** Mapping a page may involve an physical page allocation (for a new
     PT), hence may potentially block */
 #define SOS_VM_MAP_ATOMIC     (1<<31)
 
-/** Virtual address where the mirroring takes place */
-#define SOS_PAGING_MIRROR_VADDR 0x3fc00000 /* 1GB - 4MB */
-/** 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 */
 
 /**
  * Setup initial page directory structure where the kernel is
@@ -94,14 +108,41 @@
 /**
  * Undo the mapping from vaddr to the underlying physical page (if any)
  * @param vpage_vaddr  The address of the virtual page (page-aligned)
+ *
+ * @return >= 0 when OK (the number of bytes of RAM unmapped), < 0 on error
  */
 sos_ret_t sos_paging_unmap(sos_vaddr_t vpage_vaddr);
 
 /**
+ * Undo the mapping from [vaddr .. vaddr + size[ to the underlying
+ * physical pages (if any)
+ * @param vpage_vaddr The address of the virtual page (page-aligned)
+ * @param size        The size (in bytes) to unmap. MUST be page-aligned
+ */
+sos_ret_t sos_paging_unmap_interval(sos_vaddr_t base_vpage_vaddr,
+				    sos_size_t  size);
+
+/**
  * 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);
+sos_ui32_t sos_paging_get_prot(sos_vaddr_t vaddr);
+
+/**
+ * Change the page access rights
+ */
+sos_ret_t sos_paging_set_prot(sos_vaddr_t vaddr,
+			      sos_ui32_t  new_prot);
+
+/**
+ * Change the access rights of the mapping from [vaddr .. vaddr +
+ * size[ to the underlying physical pages (if any)
+ * @param vpage_vaddr The address of the virtual page (page-aligned)
+ * @param size        The size (in bytes) to unmap. MUST be page-aligned
+ */
+sos_ret_t sos_paging_set_prot_of_interval(sos_vaddr_t vaddr,
+					  sos_size_t  size,
+					  sos_ui32_t  new_prot);
 
 /**
  * Return the physical address of the given virtual address. Since page
@@ -117,4 +158,37 @@
   (sos_paging_get_paddr(vaddr) != NULL)
 
 
+/* *************************************************
+ * Functions restricted to mm_context module
+ */
+
+
+/**
+ * Release the references to all the referenced pages (and PT on
+ * x86). On x86, this applies only to the USER pages and PT.
+ */
+sos_ret_t sos_paging_dispose(sos_vaddr_t vaddr_PD);
+
+
+/**
+ * Copy the MMU configuration related to the kernel virtual area
+ */
+sos_ret_t sos_paging_copy_kernel_space(sos_vaddr_t dest_vaddr_PD,
+				       sos_vaddr_t src_vaddr_PD);
+
+
+/**
+ * Retrieve the current physical address of the PD
+ */
+sos_paddr_t sos_paging_get_current_PD_paddr();
+
+
+/**
+ * Change the current MMU configuration.
+ *
+ * @note DANGEROUS. Don't use it unless you know exactly what you're
+ * doing !
+ */
+sos_ret_t sos_paging_set_current_PD_paddr(sos_paddr_t paddr_PD);
+
 #endif /* _SOS_PAGING_H_ */
diff -ruN /tmp/sos-code-article6.75/hwcore/segment.h ../sos-code-article7/hwcore/segment.h
--- /tmp/sos-code-article6.75/hwcore/segment.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/hwcore/segment.h	2005-04-27 20:14:19.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The SOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -30,18 +29,21 @@
  * @see Intel x86 doc, vol 3 chapter 3.
  */
 
-#include <sos/types.h>
 
 /*
  * 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 */
+#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 */
+#define SOS_SEG_UCODE      3 /* User code segment */
+#define SOS_SEG_UDATA      4 /* User data segment */
+#define SOS_SEG_KERNEL_TSS 5 /* Kernel TSS for CPL3 -> CPL0 privilege change */
 
 
+#ifndef ASM_SOURCE
 /**
  * Helper macro that builds a segment register's value
  */
@@ -49,6 +51,18 @@
   (  (((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
 
 
 /*
diff -ruN /tmp/sos-code-article6.75/hwcore/swintr.c ../sos-code-article7/hwcore/swintr.c
--- /tmp/sos-code-article6.75/hwcore/swintr.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/hwcore/swintr.c	2005-04-27 20:14:19.000000000 +0200
@@ -0,0 +1,50 @@
+/* Copyright (C) 2005  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/uaccess.h>
+
+#include "idt.h"
+#include "irq.h"
+
+#include "swintr.h"
+
+/**
+ * The Assembler low-level wrapper for the SOS syscalls
+ *
+ * This wrapper takes care of saving the state of the calling user
+ * thread before calling the "C" function sos_do_syscall(), passing it
+ * the correct arguments.
+ */
+extern void sos_syscall_wrapper();
+
+
+sos_ret_t sos_swintr_subsystem_setup(void)
+{
+  sos_ui32_t flags;
+  sos_ret_t retval;
+
+  sos_disable_IRQs(flags);
+
+  retval
+    = sos_idt_set_handler(SOS_SWINTR_SOS_SYSCALL,
+			  (sos_vaddr_t) sos_syscall_wrapper,
+			  3 /* CPL3 routine */);
+
+  sos_restore_IRQs(flags);
+
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/hwcore/swintr.h ../sos-code-article7/hwcore/swintr.h
--- /tmp/sos-code-article6.75/hwcore/swintr.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/hwcore/swintr.h	2005-04-27 20:14:20.000000000 +0200
@@ -0,0 +1,44 @@
+/* Copyright (C) 2005  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_SWINTR_H_
+#define _SOS_SWINTR_H_
+
+/**
+ * @file swintr.h
+ *
+ * SOS handlers for the software interrupts. In SOS, we handle a
+ * single software interrupt: the syscall interrupt.
+ */
+
+
+/**
+ * The SOS software interrupt number for issueing syscalls
+ */
+#define SOS_SWINTR_SOS_SYSCALL  0x42
+
+
+#if defined(KERNEL_SOS) && !defined(ASM_SOURCE)
+
+#include <hwcore/cpu_context.h>
+#include <sos/errno.h>
+
+sos_ret_t sos_swintr_subsystem_setup(void);
+
+#endif /* defined(KERNEL_SOS) && !defined(ASM_SOURCE) */
+
+#endif /* _SOS_SWINTR_H_ */
diff -ruN /tmp/sos-code-article6.75/hwcore/swintr_wrappers.S ../sos-code-article7/hwcore/swintr_wrappers.S
--- /tmp/sos-code-article6.75/hwcore/swintr_wrappers.S	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/hwcore/swintr_wrappers.S	2005-04-27 20:14:20.000000000 +0200
@@ -0,0 +1,116 @@
+/* Copyright (C) 2005  David Decotigny
+   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 "segment.h"
+
+
+/**
+ * @file swintr_wrappers.S
+ *
+ * The SOS low-level handlers for the software interrupts. Currently
+ * only 1 wrapper: that for the SOS syscalls.
+ */
+.file "swintr_wrappers.S"
+
+.text
+
+/* The address of the real "C" syscall function */
+.extern sos_do_syscall
+
+/** Update the kernel TSS in case we are switching to a thread in user
+	mode in order to come back into the correct kernel stack */
+.extern sos_cpu_context_update_kernel_tss
+
+/* The address of the function to call to set back the user thread's
+   MMU configuration upon return to user context */
+.extern sos_thread_prepare_syscall_switch_back
+
+.p2align 2, 0x90
+.globl sos_syscall_wrapper
+sos_syscall_wrapper:
+.type sos_syscall_wrapper,@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
+
+  /* Set correct kernel segment descriptors' value */
+  movw $SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA), %di
+  pushw %di ; popw %ds
+  pushw %di ; popw %es
+  pushw %di ; popw %fs
+  pushw %di ; popw %gs
+
+  /* Prepare the call to do_syscall */ 
+  pushl %esp /* user_ctxt */
+  pushl %eax /* syscall ID */
+
+  call  sos_do_syscall
+  /* Unallocate the stack used by the
+     do_syscall arguments */
+  addl  $8, %esp
+
+  /* store the do_syscall return value into interrupted context */
+  movl %eax, 12(%esp)
+
+  /* Set the MMU configuration to that of the user thread's process */
+  pushl %esp /* user_ctxt */
+  call sos_thread_prepare_syscall_switch_back
+  addl  $4, %esp /* Unallocate the stack */
+
+  /* Prepare kernel TSS because we are switching back to a user
+     thread: we make sure that we will come back into the kernel at a
+     correct stack location */
+  pushl %esp /* Pass the location of the context we are
+  	        restoring to the function */
+  call sos_cpu_context_update_kernel_tss
+  addl $4, %esp
+
+  /* Restore the user context */
+  popw  %gs
+  popw  %fs
+  popw  %es
+  popw  %ds
+  popw  %ss
+  addl  $2,%esp
+  popl  %eax /* This is the return value of do_syscall (see above) */
+  popl  %ebx
+  popl  %ecx
+  popl  %edx
+  popl  %esi
+  popl  %edi
+ 
+  popl  %ebp
+  /* Remove fake error code */
+  addl $4, %esp
+  iret
diff -ruN /tmp/sos-code-article6.75/INSTALL ../sos-code-article7/INSTALL
--- /tmp/sos-code-article6.75/INSTALL	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/INSTALL	2005-04-27 20:14:16.000000000 +0200
@@ -114,5 +114,18 @@
   use the method 2/ above.
 
 
+NOTE : recommended versions of the tools
+----------------------------------------
+ - OS           : Linux 2.6.11.7-d2-1 i686
+ - gcc          : gcc (GCC) 3.3.5 (Debian 1:3.3.5-8)
+ - GNU binutils : GNU ld version 2.15
+ - GNU make     : GNU Make 3.80
+
+Also tested with (on ppc/debian host):
+ - OS           : Linux 2.6.10-powerpc ppc
+ - gcc          : gcc (GCC) 3.2.2
+ - GNU binutils : GNU ld version 2.13.2
+ - GNU make     : GNU Make 3.80
+
 --
 David Decotigny
diff -ruN /tmp/sos-code-article6.75/Makefile ../sos-code-article7/Makefile
--- /tmp/sos-code-article6.75/Makefile	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/Makefile	2005-04-27 20:14:16.000000000 +0200
@@ -1,18 +1,42 @@
+## Copyright (C) 2004,2005  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. 
+
 CC=gcc
-CFLAGS  = -Wall -nostdlib -nostdinc -ffreestanding -DKERNEL_SOS
-LDFLAGS = --warn-common
+LD=ld
+CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS
+LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
+LDFLAGS = --warn-common -nostdlib
 OBJECTS = bootstrap/multiboot.o					\
           hwcore/idt.o hwcore/gdt.o				\
+	  hwcore/swintr.o hwcore/swintr_wrappers.o		\
 	  hwcore/exception.o hwcore/exception_wrappers.o	\
 	  hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o	\
-	  hwcore/paging.o					\
+	  hwcore/paging.o 			                \
 	  hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o	\
 	  hwcore/cpu_context.o hwcore/cpu_context_switch.o	\
+	  hwcore/mm_context.o					\
 	  sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o		\
 	  sos/physmem.o sos/klibc.o				\
-	  sos/kthread.o sos/kwaitq.o				\
+	  sos/thread.o sos/kwaitq.o				\
           sos/time.o sos/sched.o sos/ksynch.o			\
-          sos/assert.o sos/main.o sos/mouse_sim.o
+	  sos/process.o sos/syscall.o				\
+          sos/assert.o sos/main.o sos/mouse_sim.o               \
+	  sos/uaccess.o sos/calcload.o				\
+          userland/userprogs.kimg sos/test-art7.o
 
 KERNEL_OBJ   = sos.elf
 MULTIBOOT_IMAGE = fd.img
@@ -25,12 +49,16 @@
 	./support/build_image.sh $@ $<
 
 $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds
-	$(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS)
+	$(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) $(LIBGCC)
 	-nm -C $@ | cut -d ' ' -f 1,3 > sos.map
 	size $@
 
 -include .mkvars
 
+# Create the userland programs to include in the kernel image
+userland/userprogs.kimg: FORCE
+	$(MAKE) -C userland
+
 # Create objects from C source code
 %.o: %.c
 	$(CC) -I$(PWD) -c $< $(CFLAGS) -o $@
@@ -39,6 +67,9 @@
 %.o: %.S
 	$(CC) -I$(PWD) -c $< $(CFLAGS) -DASM_SOURCE=1 -o $@
 
+FORCE:
+	@
+
 # Clean directory
 clean:
 	$(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.map
@@ -49,3 +80,4 @@
 	$(RM) sos/*.o sos/*~
 	$(RM) support/*~
 	$(RM) extra/*~
+	$(MAKE) -C userland clean
diff -ruN /tmp/sos-code-article6.75/sos/assert.c ../sos-code-article7/sos/assert.c
--- /tmp/sos-code-article6.75/sos/assert.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/assert.c	2005-04-27 20:14:22.000000000 +0200
@@ -19,6 +19,7 @@
 #include <sos/klibc.h>
 #include <drivers/bochs.h>
 #include <drivers/x86_videomem.h>
+#include <sos/thread.h> // backtrace
 
 #include "assert.h"
 
@@ -34,10 +35,12 @@
   va_end(ap);
 
   sos_bochs_putstring(buff); sos_bochs_putstring("\n");
-  sos_x86_videomem_putstring(24, 0,
+  sos_x86_videomem_putstring(23, 0,
 			     SOS_X86_VIDEO_BG_BLACK
 			     | SOS_X86_VIDEO_FG_LTRED , buff);
 
+  sos_thread_dump_backtrace(TRUE, TRUE);
+
   /* Infinite loop: processor halted */
   for ( ; ; )
     asm("hlt\n");
diff -ruN /tmp/sos-code-article6.75/sos/calcload.c ../sos-code-article7/sos/calcload.c
--- /tmp/sos-code-article6.75/sos/calcload.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/calcload.c	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,396 @@
+/* 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 <hwcore/irq.h>
+#include <sos/kmalloc.h>
+#include <sos/assert.h>
+#include <sos/calcload.h>
+
+
+/**
+ * Multiplicative factor to display digits after the decimal dot. The
+ * higher the value, the higher the precision, but the higher the risk
+ * that the value you get is incorrect (integer overflow).
+ *
+ * The CPU ratios will be correctly displayed as long as:
+ *    2^32 > (900 * HZ * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
+ * The "900" above means 900s because the load is computed over 15mn (900s).
+ * HZ is the frequency of the timer tick because the load is updated
+ * at each timer tick.
+ * The "100" above is the multiplication factor to get the ratio value
+ * between 0 and 100 (instead of 0-1).
+ *
+ * The maximum CPU sustainable load that will be correctly displayed
+ * is given by the formula:
+ *    (2^32 - 1) / (900 * HZ * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
+ * With HZ=100, these maximum sustainable loads are respectively
+ * 47.721, 477.21 and 4772.1 with
+ * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR being respectively 1000, 100
+ * and 10.
+ *
+ * Hence, among these formulaes, the most limitative one is that
+ * concerning the CPU ratios (because of the "100" factor). Actually,
+ * for HZ=100, the only correct value is 10.
+ */
+#define SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR 10 /* 1/10 resolution */
+
+
+/**
+ * To compute the load, at each clock tick we store the number of
+ * threads ready in kernel/user mode, and the kind of the thread that
+ * is executing (user or kernel mode): this is stored in
+ * current_load_entry. We then compute the sum of these numbers over 3
+ * periods of time: 1 minute, 5 minutes, 15 minutes. This is the role
+ * of the sliding windows data structures. A "sliding window" is only
+ * the synthetic sum of these figures, not a real sliding window of
+ * load_entries. At each timer tick and everytime the load is updated,
+ * the computations are in O(1).
+ *
+ * All the sliding windows share the main "recorded_load" array of
+ * load_entries for that; its role is to store the last 15mns of load
+ * data, which encompasses the data for the 1mn, 5mn and 15mn sliding
+ * windows.
+ */
+
+/* Max number of seconds that we record (ie number of entries in
+   recorded_loads) */
+#define NB_SECS 900
+
+
+/* An entry in the longest sliding window */
+struct load_entry
+{
+  sos_ui32_t nb_ticks;
+
+  sos_ui32_t nb_user_running;
+  sos_ui32_t nb_kernel_running;
+
+  sos_ui32_t nb_user_ready;
+  sos_ui32_t nb_kernel_ready;
+};
+
+struct load_entry current_load_entry;
+
+
+/* The longest sliding window */
+struct recorded_loads
+{
+  sos_ui32_t most_recent;
+  sos_ui32_t max_entries;
+
+  struct load_entry *load_entries;
+};
+
+#define LOAD_GET_ENTRY(loads,age) \
+  (&((loads).load_entries[( (loads).max_entries + (loads).most_recent - (age))\
+                          % ((loads).max_entries)]))
+
+/* A sliding window, we manage one for each time interval */
+struct sliding_window
+{
+  sos_ui32_t max_entries;
+  sos_ui32_t nb_entries;
+
+  sos_ui32_t sigma_nb_ticks;
+  sos_ui32_t sigma_nb_user_running;
+  sos_ui32_t sigma_nb_kernel_running;
+  sos_ui32_t sigma_nb_user_ready;
+  sos_ui32_t sigma_nb_kernel_ready;
+};
+
+
+/* The main sliding window */
+static struct recorded_loads recorded_loads;
+
+/* The sliding windows for 3 tims intervals: 1min, 5min, 15min */
+static struct sliding_window load_1mn, load_5mn, load_15mn;
+
+/* Forward declaration */
+static struct sos_timeout_action calcload_timeout;
+static void calcload_routine(struct sos_timeout_action *a);
+
+
+static void _reinit_load_subsystem()
+{
+  memset(& recorded_loads, 0x0, sizeof(recorded_loads));
+  memset(& current_load_entry, 0x0, sizeof(struct load_entry));
+  memset(& load_1mn, 0x0, sizeof(load_1mn));
+  memset(& load_5mn, 0x0, sizeof(load_5mn));
+  memset(& load_15mn, 0x0, sizeof(load_15mn));
+}
+
+
+sos_ret_t sos_load_subsystem_setup(void)
+{
+  struct sos_time period;
+  _reinit_load_subsystem();
+
+  if (recorded_loads.load_entries)
+    sos_kfree((sos_vaddr_t) recorded_loads.load_entries);
+  _reinit_load_subsystem();
+
+  /* Allocate 900 entries to store 15mn of data (because 15minutes =
+     900s) */
+  recorded_loads.max_entries = NB_SECS;
+  recorded_loads.load_entries
+    = (struct load_entry*) sos_kmalloc(NB_SECS * sizeof(struct load_entry),
+				       0);
+  if (! recorded_loads.load_entries)
+    {
+      return -SOS_ENOMEM;
+    }
+
+  /* Compute the number of entries in each sliding window */
+  load_1mn.max_entries  = 60;
+  load_5mn.max_entries  = 300;
+  load_15mn.max_entries = 900;
+
+  /* Program the load computation action */
+  sos_time_init_action(& calcload_timeout);
+  period.sec = 1; period.nanosec = 0;
+  return sos_time_register_action_relative(& calcload_timeout,
+					   & period,
+					   calcload_routine,
+					   NULL);
+}
+
+
+/* Shift the given sliding window to record the current_load_entry */
+static void update_sliding_window(struct sliding_window *w)
+{
+  /*
+   * Compute the value of the sum over the sliding window
+   */
+
+  /* Take the new value into account */
+  w->sigma_nb_ticks          += current_load_entry.nb_ticks;
+  w->sigma_nb_user_running   += current_load_entry.nb_user_running;
+  w->sigma_nb_kernel_running += current_load_entry.nb_kernel_running;
+  w->sigma_nb_user_ready     += current_load_entry.nb_user_ready;
+  w->sigma_nb_kernel_ready   += current_load_entry.nb_kernel_ready;
+
+  /* Remove the oldest entry, if it is going to be popped out of the
+     sliding window */
+  if (w->nb_entries < w->max_entries)
+    {
+      w->nb_entries ++;
+    }
+  else
+    {
+      struct load_entry * oldest_entry;
+      oldest_entry = LOAD_GET_ENTRY(recorded_loads, w->nb_entries - 1);
+      w->sigma_nb_ticks          -= oldest_entry->nb_ticks;
+      w->sigma_nb_user_running   -= oldest_entry->nb_user_running;
+      w->sigma_nb_kernel_running -= oldest_entry->nb_kernel_running;
+      w->sigma_nb_user_ready     -= oldest_entry->nb_user_ready;
+      w->sigma_nb_kernel_ready   -= oldest_entry->nb_kernel_ready;
+    }
+}
+
+
+/* The timeout action responsible for computing the CPU load */
+static void calcload_routine(struct sos_timeout_action *a)
+{
+  struct load_entry * new_head;
+  struct sos_time delay;
+
+  if (! recorded_loads.load_entries)
+    return;
+
+  /* Update the sliding windows */
+  update_sliding_window(& load_1mn);
+  update_sliding_window(& load_5mn);
+  update_sliding_window(& load_15mn);
+
+  /* Move the head of the list forward */
+  recorded_loads.most_recent
+    = (recorded_loads.most_recent + 1) % recorded_loads.max_entries;
+
+  /* Update the new head */
+  new_head = & recorded_loads.load_entries[recorded_loads.most_recent];
+  memcpy(new_head, & current_load_entry, sizeof(current_load_entry));
+
+  /* Reset the current load entry */
+  memset(& current_load_entry, 0x0, sizeof(current_load_entry));
+
+  /* Program next occurence of the action */
+  delay.sec = 1;
+  delay.nanosec = 0;
+  sos_time_register_action_relative(a, & delay, calcload_routine, NULL);
+}
+
+
+sos_ret_t sos_load_do_timer_tick(sos_bool_t cur_is_user,
+				 sos_ui32_t nb_user_ready,
+				 sos_ui32_t nb_kernel_ready)
+{
+  sos_ui32_t flags;
+
+  sos_disable_IRQs(flags);
+  current_load_entry.nb_ticks ++;
+  current_load_entry.nb_user_ready += nb_user_ready;
+  current_load_entry.nb_kernel_ready += nb_kernel_ready;
+  if (cur_is_user)
+    current_load_entry.nb_user_running ++;
+  else
+    current_load_entry.nb_kernel_running ++;
+  sos_restore_IRQs(flags);
+
+  return SOS_OK;
+}
+
+
+void sos_load_to_string(char dest[11], sos_ui32_t load_value)
+{
+  sos_bool_t print0 = FALSE;
+  sos_ui32_t d;
+
+#define PUTCH(c) ({ *dest = (c); dest ++; })
+
+  for (d = 1000000000UL ; d > 0 ; d /= 10)
+    {
+      sos_ui32_t digit = (load_value / d) % 10;
+
+      if (digit > 0)
+	{
+	  PUTCH(digit + '0');
+	  print0 = TRUE;
+	}
+      else if (print0)
+	PUTCH('0');
+      
+      if (d == SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
+	{
+	  if (! print0)
+	    PUTCH('0');
+
+	  PUTCH('.');
+	  print0 = TRUE;
+	}
+    }
+  *dest = '\0';
+}
+
+
+void sos_load_get_uload(sos_ui32_t * _load_1mn,
+			sos_ui32_t * _load_5mn,
+			sos_ui32_t * _load_15mn)
+{
+  sos_ui32_t flags;
+
+  if (load_1mn.sigma_nb_ticks < 1)
+    return;
+
+  sos_disable_IRQs(flags);
+  *_load_1mn  = ( load_1mn.sigma_nb_user_ready
+		  + load_1mn.sigma_nb_user_running)
+                * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_1mn.sigma_nb_ticks;
+  *_load_5mn  = ( load_5mn.sigma_nb_user_ready
+		  + load_5mn.sigma_nb_user_running)
+                * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_5mn.sigma_nb_ticks;
+  *_load_15mn  = ( load_15mn.sigma_nb_user_ready
+		   + load_15mn.sigma_nb_user_running)
+                 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                 / load_15mn.sigma_nb_ticks;
+  sos_restore_IRQs(flags);
+}
+
+
+void sos_load_get_sload(sos_ui32_t * _load_1mn,
+			sos_ui32_t * _load_5mn,
+			sos_ui32_t * _load_15mn)
+{
+  sos_ui32_t flags;
+
+  if (load_1mn.sigma_nb_ticks < 1)
+    return;
+
+  /* The "IDLE" thread is always either ready or running by definition */
+  SOS_ASSERT_FATAL(load_1mn.sigma_nb_kernel_ready
+		   + load_1mn.sigma_nb_kernel_running
+		   >= load_1mn.sigma_nb_ticks);
+
+  /* Remove the IDLE thread from the load calculation */
+  sos_disable_IRQs(flags);
+  *_load_1mn  = ( load_1mn.sigma_nb_kernel_ready
+		  + load_1mn.sigma_nb_kernel_running
+		  - load_1mn.sigma_nb_ticks)
+                * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_1mn.sigma_nb_ticks;
+  *_load_5mn  = ( load_5mn.sigma_nb_kernel_ready
+		  + load_5mn.sigma_nb_kernel_running
+		  - load_5mn.sigma_nb_ticks)
+                * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_5mn.sigma_nb_ticks;
+  *_load_15mn  = ( load_15mn.sigma_nb_kernel_ready
+		   + load_15mn.sigma_nb_kernel_running
+		   - load_15mn.sigma_nb_ticks)
+                 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                 / load_15mn.sigma_nb_ticks;
+  sos_restore_IRQs(flags);
+}
+
+
+void sos_load_get_uratio(sos_ui32_t * _load_1mn,
+			 sos_ui32_t * _load_5mn,
+			 sos_ui32_t * _load_15mn)
+{
+  sos_ui32_t flags;
+
+  if (load_1mn.sigma_nb_ticks < 1)
+    return;
+
+  sos_disable_IRQs(flags);
+  *_load_1mn  = load_1mn.sigma_nb_user_running
+                * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_1mn.sigma_nb_ticks;
+  *_load_5mn  = load_5mn.sigma_nb_user_running
+                * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_5mn.sigma_nb_ticks;
+  *_load_15mn  = load_15mn.sigma_nb_user_running
+                 * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                 / load_15mn.sigma_nb_ticks;
+  sos_restore_IRQs(flags);
+}
+
+
+void sos_load_get_sratio(sos_ui32_t * _load_1mn,
+			 sos_ui32_t * _load_5mn,
+			 sos_ui32_t * _load_15mn)
+{
+  sos_ui32_t flags;
+
+  if (load_1mn.sigma_nb_ticks < 1)
+    return;
+
+  /* Don't remove the CPU occupation ration of the IDLE thread
+     here... */
+  sos_disable_IRQs(flags);
+  *_load_1mn  = load_1mn.sigma_nb_kernel_running
+                * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_1mn.sigma_nb_ticks;
+  *_load_5mn  = load_5mn.sigma_nb_kernel_running
+                * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                / load_5mn.sigma_nb_ticks;
+  *_load_15mn  = load_15mn.sigma_nb_kernel_running
+                 * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+                 / load_15mn.sigma_nb_ticks;
+  sos_restore_IRQs(flags);
+}
diff -ruN /tmp/sos-code-article6.75/sos/calcload.h ../sos-code-article7/sos/calcload.h
--- /tmp/sos-code-article6.75/sos/calcload.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/calcload.h	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,117 @@
+/* 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_CPULOAD_H_
+#define _SOS_CPULOAD_H_
+
+#include <sos/errno.h>
+#include <sos/types.h>
+#include <sos/time.h>
+
+
+/**
+ * @file calcload.h
+ *
+ * Management of the CPU load in the system. For three intervals
+ * (1min, 5min, 15min), we maintain the user/kernel loads (ie number
+ * of threads in user/kernel mode ready or running) and the
+ * user/kernel CPU occupation ratio.
+ */
+
+
+/**
+ * Reinitialize the calcload subsystem. Must be called after the time
+ * subsystem has been initialized
+ */
+sos_ret_t sos_load_subsystem_setup(void);
+
+
+/**
+ * Get the current USER load for each of the intervals.  Definition:
+ * the USER load is the mean number of threads in USER mode which are
+ * ready or running over the period.
+ *
+ * @return the current USER load * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+ */
+void sos_load_get_uload(sos_ui32_t * load_1mn,
+			sos_ui32_t * load_5mn,
+			sos_ui32_t * load_15mn);
+
+
+/**
+ * Get the current KERNEL load for each of the intervals.  Definition:
+ * the KERNEL load is the mean number of threads in KERNEL mode which are
+ * ready or running over the period.
+ *
+ * @note The load of the IDLE thread is removed from this computation !
+ *
+ * @return the current KERNEL load * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+ */
+void sos_load_get_sload(sos_ui32_t * load_1mn,
+			sos_ui32_t * load_5mn,
+			sos_ui32_t * load_15mn);
+
+
+/**
+ * Get the current User CPU occupation ratio
+ *
+ * @return the current User/Kernel CPU occupation ratio
+ *                     * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+ */
+void sos_load_get_uratio(sos_ui32_t * load_1mn,
+			  sos_ui32_t * load_5mn,
+			  sos_ui32_t * load_15mn);
+
+
+/**
+ * Get the current Kernel CPU occupation ratio
+ *
+ * @note The load of the IDLE thread is NOT removed from this computation !
+ *
+ * @return the current User/Kernel CPU occupation ratio
+ *                     * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
+ */
+void sos_load_get_sratio(sos_ui32_t * load_1mn,
+			  sos_ui32_t * load_5mn,
+			  sos_ui32_t * load_15mn);
+
+
+/**
+ * Generate the "dest" string with the string corresponding to
+ * load_value / SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR with decimal
+ * digits
+ */
+void sos_load_to_string(char dest[11], sos_ui32_t load_value);
+
+
+/* ******************************************************
+ * Restricted function. Used only by sched.c/time.c
+ */
+
+
+/**
+ * Restricted callback called from the scheduler subsystem to update
+ * the load parameters.
+ *
+ * @note This is RESTRICTED function to be used by time.c
+ */
+sos_ret_t sos_load_do_timer_tick(sos_bool_t cur_is_user,
+				 sos_ui32_t nb_user_ready,
+				 sos_ui32_t nb_kernel_ready);
+
+
+#endif /* _SOS_CPULOAD_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/errno.h ../sos-code-article7/sos/errno.h
--- /tmp/sos-code-article6.75/sos/errno.h	2005-01-04 04:13:52.000000000 +0100
+++ ../sos-code-article7/sos/errno.h	2005-04-27 20:14:20.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The SOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -33,6 +32,7 @@
 #define SOS_EBUSY  4   /* Object or device still in use */
 #define SOS_EINTR  5   /* Wait/Sleep has been interrupted */
 #define SOS_EPERM  6   /* Mutex/files ownership error */
+#define SOS_EFAULT 7   /* Unresolved virtual memory fault */
 #define SOS_EFATAL 255 /* Internal fatal error */
 
 /* A negative value means that an error occured.  For
diff -ruN /tmp/sos-code-article6.75/sos/klibc.c ../sos-code-article7/sos/klibc.c
--- /tmp/sos-code-article6.75/sos/klibc.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/klibc.c	2005-04-27 20:14:20.000000000 +0200
@@ -181,6 +181,9 @@
 int vsnprintf(char *buff, sos_size_t len, const char * format, va_list ap)
 {
   sos_size_t i, result;
+  sos_bool_t fmt_modifiers = FALSE;
+  sos_bool_t prefix_long = FALSE;
+  sos_bool_t prefix_long_long = FALSE;
   
   if (!buff || !format || (len < 0))
     return -1;
@@ -193,104 +196,191 @@
   } while (0)
   
   result = 0;
-  for(i=0 ; format[i] != '\0' ; i++){
-    switch (format[i])
-      {
-      case '%':
-	i++;
-	switch(format[i])
-	  {
-	  case '%':
+  for(i=0 ; format[i] != '\0' ; i++)
+    {
+      if (!fmt_modifiers && (format[i] != '%'))
+	{
+	  PUTCHAR(format[i]);
+	  continue;
+	}
+	
+      switch (format[i])
+	{
+	case '%':
+	  if (fmt_modifiers)
 	    {
 	      PUTCHAR('%');
+	      fmt_modifiers = FALSE;
 	      break;
 	    }
-	  case 'i':;
-	  case 'd':
-	    {
-	      int integer = va_arg(ap,int);
-	      int cpt2 = 0;
-	      char buff_int[16];
-	      
-	      if (integer<0)
-		PUTCHAR('-');
-	      /* Ne fait pas integer = -integer ici parce que INT_MIN
-		 n'a pas d'equivalent positif (int = [-2^31, 2^31-1]) */
-	      
-	      do {
-		int m10 = integer%10;
-		m10 = (m10 < 0)? -m10:m10;
-		buff_int[cpt2++]=(char)('0'+ m10);
-		integer=integer/10;
-	      } while(integer!=0);
-	      
-	      for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
-		PUTCHAR(buff_int[cpt2]);
-	      
-	      break;
-	    }
+	  
+	  fmt_modifiers    = TRUE;
+	  prefix_long      = FALSE;
+	  prefix_long_long = FALSE;
+	  break;
+	  
+	case 'l':
+	  if (prefix_long)
+	    prefix_long_long = TRUE;
+	  else
+	    prefix_long = TRUE;
+	  break;
+	  
+	case 'u':
+	  {
+	    if (! prefix_long_long)
+	      {
+		unsigned int integer = va_arg(ap,unsigned int);
+		int cpt2 = 0;
+		char buff_int[16];
+		
+		do {
+		  int m10 = integer%10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
 	    
-	  case 'c':
-	    {
-	      int value = va_arg(ap,int);
-	      PUTCHAR((char)value);
-	      break;
-	    }
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	    else
+	      {
+		unsigned long long int integer
+		  = va_arg(ap,unsigned long long int);
+		int cpt2 = 0;
+		char buff_int[32];
+		
+		do {
+		  int m10 = integer%10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
 	    
-	  case 's':
-	    {
-	      char *string = va_arg(ap,char *);
-	      if (! string)
-		string = "(null)";
-	      for( ; *string != '\0' ; string++)
-		PUTCHAR(*string);
-	      break;
-	    }
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	  }
+	  fmt_modifiers = FALSE;
+	  break;
 
-	  case 'p':
-	    PUTCHAR('0');
-	    PUTCHAR('x');
-	  case 'x':
-	    {
-	      unsigned int hexa = va_arg(ap,int);
-	      unsigned int nb;
-	      int i, had_nonzero = 0;
-	      for(i=0 ; i < 8 ; i++)
-		{
-		  nb = (unsigned int)(hexa << (i*4));
-		  nb = (nb >> 28) & 0xf;
-		  // Skip the leading zeros
-		  if (nb == 0)
-		    {
-		      if (had_nonzero)
-			PUTCHAR('0');
-		    }
-		  else
-		    {
-		      had_nonzero = 1;
-		      if (nb < 10)
-			PUTCHAR('0'+nb);
-		      else
-			PUTCHAR('a'+(nb-10));
-		    }
-		}
-	      if (! had_nonzero)
-		PUTCHAR('0');
-	      break;
-	    }
+	case 'i':
+	case 'd':
+	  {
+	    if (! prefix_long_long)
+	      {
+		int integer = va_arg(ap,int);
+		int cpt2 = 0;
+		char buff_int[16];
+		
+		if (integer<0)
+		  PUTCHAR('-');
+		/* Ne fait pas integer = -integer ici parce que INT_MIN
+		   n'a pas d'equivalent positif (int = [-2^31, 2^31-1]) */
+		
+		do {
+		  int m10 = integer%10;
+		  m10 = (m10 < 0)? -m10:m10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	    else
+	      {
+		long long int integer = va_arg(ap,long long int);
+		int cpt2 = 0;
+		char buff_int[32];
+		
+		if (integer<0)
+		  PUTCHAR('-');
+		/* Ne fait pas integer = -integer ici parce que INT_MIN
+		   n'a pas d'equivalent positif (int = [-2^63, 2^63-1]) */
+		
+		do {
+		  int m10 = integer%10;
+		  m10 = (m10 < 0)? -m10:m10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	  }
+	  fmt_modifiers = FALSE;
+	  break;
+	  
+	case 'c':
+	  {
+	    int value = va_arg(ap,int);
+	    PUTCHAR((char)value);
+	    fmt_modifiers = FALSE;
 	    break;
-	
-	  default:
-            PUTCHAR('%');
-	    PUTCHAR(format[i]);
 	  }
-	break;
-	
-      default:
-        PUTCHAR(format[i]);
-      }
-  }
-  
+	  
+	case 's':
+	  {
+	    char *string = va_arg(ap,char *);
+	    if (! string)
+	      string = "(null)";
+	    for( ; *string != '\0' ; string++)
+	      PUTCHAR(*string);
+	    fmt_modifiers = FALSE;
+	    break;
+	  }
+	  
+	case 'p':
+	  PUTCHAR('0');
+	  PUTCHAR('x');
+	case 'x':
+	  {
+	    unsigned long long int hexa;
+	    unsigned long long int nb;
+	    int i, had_nonzero = 0;
+	    
+	    if (prefix_long_long)
+	      hexa = va_arg(ap,unsigned long long int);
+	    else
+	      hexa = va_arg(ap,unsigned int);
+	    
+	    for(i=0 ; i < 16 ; i++)
+	      {
+		nb = (unsigned long long int)(hexa << (i*4));
+		nb = (nb >> 60) & 0xf;
+		// Skip the leading zeros
+		if (nb == 0)
+		  {
+		    if (had_nonzero)
+		      PUTCHAR('0');
+		  }
+		else
+		  {
+		    had_nonzero = 1;
+		    if (nb < 10)
+		      PUTCHAR('0'+nb);
+		    else
+		      PUTCHAR('a'+(nb-10));
+		  }
+	      }
+	    if (! had_nonzero)
+	      PUTCHAR('0');
+	  }
+	  fmt_modifiers = FALSE;
+	  break;
+	  
+	default:
+	  PUTCHAR('%');
+	  if (prefix_long)
+	    PUTCHAR('l');
+	  if (prefix_long_long)
+	    PUTCHAR('l');
+	  PUTCHAR(format[i]);
+	  fmt_modifiers = FALSE;
+	}
+    }
+
   *buff = '\0';
   return result;
 }
diff -ruN /tmp/sos-code-article6.75/sos/kmem_slab.h ../sos-code-article7/sos/kmem_slab.h
--- /tmp/sos-code-article6.75/sos/kmem_slab.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kmem_slab.h	2005-04-27 20:14:20.000000000 +0200
@@ -73,7 +73,7 @@
 #include "kmem_vmm.h"
 
 
-/** The maximum  allowed pages for each slab */
+/** The maximum allowed pages for each slab */
 #define MAX_PAGES_PER_SLAB 32 /* 128 kB */
 
 
diff -ruN /tmp/sos-code-article6.75/sos/kmem_vmm.c ../sos-code-article7/sos/kmem_vmm.c
--- /tmp/sos-code-article6.75/sos/kmem_vmm.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kmem_vmm.c	2005-04-27 20:14:20.000000000 +0200
@@ -121,6 +121,7 @@
 
   /* First: try to retrieve the physical page mapped at this address */
   sos_paddr_t ppage_paddr = SOS_PAGE_ALIGN_INF(sos_paging_get_paddr(vaddr));
+
   if (ppage_paddr)
     {
       range = sos_physmem_get_kmem_range(ppage_paddr);
diff -ruN /tmp/sos-code-article6.75/sos/ksynch.c ../sos-code-article7/sos/ksynch.c
--- /tmp/sos-code-article6.75/sos/ksynch.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/ksynch.c	2005-04-27 20:14:21.000000000 +0200
@@ -143,7 +143,7 @@
   if (NULL != mutex->owner)
     {
       /* Owned by us or by someone else ? */
-      if (sos_kthread_get_current() == mutex->owner)
+      if (sos_thread_get_current() == mutex->owner)
 	{
 	  /* Owned by us: do nothing */
 	  retval = -SOS_EBUSY;
@@ -161,7 +161,7 @@
     }
 
   /* Ok, the mutex is available to us: take it */
-  mutex->owner = sos_kthread_get_current();
+  mutex->owner = sos_thread_get_current();
 
  exit_kmutex_lock:
   sos_restore_IRQs(flags);
@@ -180,7 +180,7 @@
   if (NULL == mutex->owner)
     {
       /* Great ! Take it now */
-      mutex->owner = sos_kthread_get_current();
+      mutex->owner = sos_thread_get_current();
 
       retval = SOS_OK;
     }
@@ -202,16 +202,34 @@
 
   sos_disable_IRQs(flags);
 
-  if (sos_kthread_get_current() != mutex->owner)
+  if (sos_thread_get_current() != mutex->owner)
     retval = -SOS_EPERM;
+
   else if (sos_kwaitq_is_empty(& mutex->kwaitq))
     {
+      /*
+       * There is NOT ANY thread waiting => we really mark the mutex
+       * as FREE
+       */
       mutex->owner = NULL;
       retval = SOS_OK;
     }
   else
-    retval = sos_kwaitq_wakeup(& mutex->kwaitq, 1, SOS_OK);
-  
+    {
+      /*
+       * There is at least 1 thread waiting => we DO NOT mark the
+       * mutex as free !
+       * Actually, we should have written:
+       *   mutex->owner = thread_that_is_woken_up;
+       * But the real Id of the next thread owning the mutex is not
+       * that important. What is important here is that mutex->owner
+       * IS NOT NULL. Otherwise there will be a possibility for the
+       * thread woken up here to have the mutex stolen by a thread
+       * locking the mutex in the meantime.
+       */
+      retval = sos_kwaitq_wakeup(& mutex->kwaitq, 1, SOS_OK);
+    } 
+ 
   sos_restore_IRQs(flags);
   return retval;
 }
diff -ruN /tmp/sos-code-article6.75/sos/ksynch.h ../sos-code-article7/sos/ksynch.h
--- /tmp/sos-code-article6.75/sos/ksynch.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/ksynch.h	2005-04-27 20:14:21.000000000 +0200
@@ -109,7 +109,7 @@
  */
 struct sos_kmutex
 {
-  struct sos_kthread *owner;
+  struct sos_thread  *owner;
   struct sos_kwaitq  kwaitq;
 };
 
diff -ruN /tmp/sos-code-article6.75/sos/kthread.c ../sos-code-article7/sos/kthread.c
--- /tmp/sos-code-article6.75/sos/kthread.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kthread.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,496 +0,0 @@
-/* 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/kmem_slab.h>
-#include <sos/kmalloc.h>
-#include <sos/klibc.h>
-#include <sos/list.h>
-#include <sos/assert.h>
-
-#include <hwcore/irq.h>
-
-#include "kthread.h"
-
-
-/**
- * The size of the stack of a kernel thread
- */
-#define SOS_KTHREAD_STACK_SIZE (1*SOS_PAGE_SIZE)
-
-
-/**
- * The identifier of the thread currently running on CPU.
- *
- * We only support a SINGLE processor, ie a SINGLE kernel thread
- * running at any time in the system. This greatly simplifies the
- * implementation of the system, since we don't have to complicate
- * things in order to retrieve the identifier of the threads running
- * on the CPU. On multiprocessor systems the current_kthread below is
- * an array indexed by the id of the CPU, so that the challenge is to
- * retrieve the identifier of the CPU. This is usually done based on
- * the stack address (Linux implementation) or on some form of TLS
- * ("Thread Local Storage": can be implemented by way of LDTs for the
- * processes, accessed through the fs or gs registers).
- */
-static volatile struct sos_kthread *current_kthread = NULL;
-
-
-/*
- * The list of kernel threads currently in the system.
- *
- * @note We could have used current_kthread for that...
- */
-static struct sos_kthread *kthread_list = NULL;
-
-
-/**
- * The Cache of kthread structures
- */
-static struct sos_kslab_cache *cache_kthread;
-
-
-struct sos_kthread *sos_kthread_get_current()
-{
-  SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
-  return (struct sos_kthread*)current_kthread;
-}
-
-
-inline static sos_ret_t _set_current(struct sos_kthread *thr)
-{
-  SOS_ASSERT_FATAL(thr->state == SOS_KTHR_READY);
-  current_kthread = thr;
-  current_kthread->state = SOS_KTHR_RUNNING;
-  return SOS_OK;
-}
-
-
-sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
-				      sos_size_t init_thread_stack_size)
-{
-  struct sos_kthread *myself;
-
-  /* Allocate the cache of kthreads */
-  cache_kthread = sos_kmem_cache_create("kthread",
-					sizeof(struct sos_kthread),
-					2,
-					0,
-					SOS_KSLAB_CREATE_MAP
-					| SOS_KSLAB_CREATE_ZERO);
-  if (! cache_kthread)
-    return -SOS_ENOMEM;
-
-  /* Allocate a new kthread structure for the current running thread */
-  myself = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread,
-						      SOS_KSLAB_ALLOC_ATOMIC);
-  if (! myself)
-    return -SOS_ENOMEM;
-
-  /* Initialize the thread attributes */
-  strzcpy(myself->name, "[kinit]", SOS_KTHR_MAX_NAMELEN);
-  myself->state           = SOS_KTHR_CREATED;
-  myself->priority        = SOS_SCHED_PRIO_LOWEST;
-  myself->stack_base_addr = init_thread_stack_base_addr;
-  myself->stack_size      = init_thread_stack_size;
-
-  /* Do some stack poisoning on the bottom of the stack, if needed */
-  sos_cpu_kstate_prepare_detect_stack_overflow(myself->cpu_kstate,
-					       myself->stack_base_addr,
-					       myself->stack_size);
-
-  /* Add the thread in the global list */
-  list_singleton_named(kthread_list, myself, gbl_prev, gbl_next);
-
-  /* Ok, now pretend that the running thread is ourselves */
-  myself->state = SOS_KTHR_READY;
-  _set_current(myself);
-
-  return SOS_OK;
-}
-
-
-struct sos_kthread *sos_kthread_create(const char *name,
-				       sos_kthread_start_routine_t start_func,
-				       void *start_arg,
-				       sos_sched_priority_t priority)
-{
-  __label__ undo_creation;
-  struct sos_kthread *new_thread;
-
-  if (! start_func)
-    return NULL;
-  if (! SOS_SCHED_PRIO_IS_VALID(priority))
-    return NULL;
-
-  /* Allocate a new kthread structure for the current running thread */
-  new_thread
-    = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread,
-						 SOS_KSLAB_ALLOC_ATOMIC);
-  if (! new_thread)
-    return NULL;
-
-  /* Initialize the thread attributes */
-  strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_KTHR_MAX_NAMELEN);
-  new_thread->state    = SOS_KTHR_CREATED;
-  new_thread->priority = priority;
-
-  /* Allocate the stack for the new thread */
-  new_thread->stack_base_addr = sos_kmalloc(SOS_KTHREAD_STACK_SIZE, 0);
-  new_thread->stack_size      = SOS_KTHREAD_STACK_SIZE;
-  if (! new_thread->stack_base_addr)
-    goto undo_creation;
-
-  /* Initialize the CPU context of the new thread */
-  if (SOS_OK
-      != sos_cpu_kstate_init(& new_thread->cpu_kstate,
-			     (sos_cpu_kstate_function_arg1_t*) start_func,
-			     (sos_ui32_t) start_arg,
-			     new_thread->stack_base_addr,
-			     new_thread->stack_size,
-			     (sos_cpu_kstate_function_arg1_t*) sos_kthread_exit,
-			     (sos_ui32_t) NULL))
-    goto undo_creation;
-
-  /* Add the thread in the global list */
-  list_add_tail_named(kthread_list, new_thread, gbl_prev, gbl_next);
-
-  /* Mark the thread ready */
-  if (SOS_OK != sos_sched_set_ready(new_thread))
-    goto undo_creation;
-
-  /* Normal non-erroneous end of function */
-  return new_thread;
-
- undo_creation:
-  sos_kmem_cache_free((sos_vaddr_t) new_thread);
-  return NULL;
-}
-
-
-/** Function called after thr has terminated. Called from inside the context
-    of another thread, interrupts disabled */
-static void delete_thread(struct sos_kthread *thr)
-{
-  list_delete_named(kthread_list, thr, gbl_prev, gbl_next);
-
-  sos_cpu_kstate_detect_stack_overflow(thr->cpu_kstate,
-				       thr->stack_base_addr,
-				       thr->stack_size);
-
-  sos_kfree((sos_vaddr_t) thr->stack_base_addr);
-  memset(thr, 0x0, sizeof(struct sos_kthread));
-  sos_kmem_cache_free((sos_vaddr_t) thr);
-}
-
-
-void sos_kthread_exit()
-{
-  sos_ui32_t flags;
-  struct sos_kthread *myself, *next_thread;
-
-  myself = sos_kthread_get_current();
-
-  /* Refuse to end the current executing thread if it still holds a
-     resource ! */
-  SOS_ASSERT_FATAL(list_is_empty_named(myself->kwaitq_list,
-				       prev_entry_for_kthread,
-				       next_entry_for_kthread));
-
-  /* Prepare to run the next thread */
-  sos_disable_IRQs(flags);
-  myself->state = SOS_KTHR_ZOMBIE;
-  next_thread = sos_reschedule(myself, FALSE);
-  _set_current(next_thread);
-
-  /* No need for sos_restore_IRQs() here because the IRQ flag will be
-     restored to that of the next thread upon context switch */
-
-  /* Immediate switch to next thread */
-  sos_cpu_kstate_exit_to(next_thread->cpu_kstate,
-			 (sos_cpu_kstate_function_arg1_t*) delete_thread,
-			 (sos_ui32_t) myself);
-}
-
-
-sos_sched_priority_t sos_kthread_get_priority(struct sos_kthread *thr)
-{
-  if (! thr)
-    thr = (struct sos_kthread*)current_kthread;
-
-  return thr->priority;
-}
-
-
-sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr)
-{
-  if (! thr)
-    thr = (struct sos_kthread*)current_kthread;
-
-  return thr->state;
-}
-
-
-typedef enum { YIELD_MYSELF, BLOCK_MYSELF } switch_type_t;
-/**
- * Helper function to initiate a context switch in case the current
- * thread becomes blocked, waiting for a timeout, or calls yield.
- */
-static sos_ret_t _switch_to_next_thread(switch_type_t operation)
-{
-  struct sos_kthread *myself, *next_thread;
-
-  SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
-
-  /* Interrupt handlers are NOT allowed to block ! */
-  SOS_ASSERT_FATAL(! sos_servicing_irq());
-
-  myself = (struct sos_kthread*)current_kthread;
-
-  /* Make sure that if we are to be marked "BLOCKED", we have any
-     reason of effectively being blocked */
-  if (BLOCK_MYSELF == operation)
-    {
-      myself->state = SOS_KTHR_BLOCKED;
-    }
-
-  /* Identify the next thread */
-  next_thread = sos_reschedule(myself, YIELD_MYSELF == operation);
-
-  /* Avoid context switch if the context does not change */
-  if (myself != next_thread)
-    {
-      /* Sanity checks for the next thread */
-      sos_cpu_kstate_detect_stack_overflow(next_thread->cpu_kstate,
-					   next_thread->stack_base_addr,
-					   next_thread->stack_size);
-      
-      /* Actual context switch */
-      _set_current(next_thread);
-      sos_cpu_kstate_switch(& myself->cpu_kstate, next_thread->cpu_kstate);
-      
-      /* Back here ! */
-      SOS_ASSERT_FATAL(current_kthread == myself);
-      SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
-    }
-  else
-    {
-      /* No context switch but still update ID of current thread */
-      _set_current(next_thread);
-    }
-
-  return SOS_OK;
-}
-
-
-/**
- * Helper function to change the thread's priority in all the
- * waitqueues associated with the thread.
- */
-static sos_ret_t _change_waitq_priorities(struct sos_kthread *thr,
-					  sos_sched_priority_t priority)
-{
-  struct sos_kwaitq_entry *kwq_entry;
-  int nb_waitqs;
-
-  list_foreach_forward_named(thr->kwaitq_list, kwq_entry, nb_waitqs,
-			     prev_entry_for_kthread, next_entry_for_kthread)
-    {
-      SOS_ASSERT_FATAL(SOS_OK == sos_kwaitq_change_priority(kwq_entry->kwaitq,
-							    kwq_entry,
-							    priority));
-    }
-
-  return SOS_OK;
-}
-
-
-sos_ret_t sos_kthread_set_priority(struct sos_kthread *thr,
-				   sos_sched_priority_t priority)
-{
-  __label__ exit_set_prio;
-  sos_ui32_t flags;
-  sos_ret_t retval;
-
-
-  if (! SOS_SCHED_PRIO_IS_VALID(priority))
-    return -SOS_EINVAL;
-
-  if (! thr)
-    thr = (struct sos_kthread*)current_kthread;
-
-  sos_disable_IRQs(flags);
-
-  /* Signal kwaitq subsystem that the priority of the thread in all
-     the waitq it is waiting in should be updated */
-  retval = _change_waitq_priorities(thr, priority);
-  if (SOS_OK != retval)
-    goto exit_set_prio;
-
-  /* Signal scheduler that the thread, currently in a waiting list,
-     should take into account the change of priority */
-  if (SOS_KTHR_READY == thr->state)
-    retval = sos_sched_change_priority(thr, priority);
-
-  /* Update priority */
-  thr->priority = priority;
-
- exit_set_prio:
-  sos_restore_IRQs(flags);
-  return retval;
-}
-
-
-sos_ret_t sos_kthread_yield()
-{
-  sos_ui32_t flags;
-  sos_ret_t retval;
-
-  sos_disable_IRQs(flags);
-
-  retval = _switch_to_next_thread(YIELD_MYSELF);
-
-  sos_restore_IRQs(flags);
-  return retval;
-}
-
-
-/**
- * Internal sleep timeout management
- */
-struct sleep_timeout_params
-{
-  struct sos_kthread *thread_to_wakeup;
-  sos_bool_t timeout_triggered;
-};
-
-
-/**
- * Callback called when a timeout happened
- */
-static void sleep_timeout(struct sos_timeout_action *act)
-{
-  struct sleep_timeout_params *sleep_timeout_params
-    = (struct sleep_timeout_params*) act->routine_data;
-
-  /* Signal that we have been woken up by the timeout */
-  sleep_timeout_params->timeout_triggered = TRUE;
-
-  /* Mark the thread ready */
-  SOS_ASSERT_FATAL(SOS_OK ==
-		   sos_kthread_force_unblock(sleep_timeout_params
-					       ->thread_to_wakeup));
-}
-
-
-sos_ret_t sos_kthread_sleep(struct sos_time *timeout)
-{
-  sos_ui32_t flags;
-  struct sleep_timeout_params sleep_timeout_params;
-  struct sos_timeout_action timeout_action;
-  sos_ret_t retval;
-
-  /* Block forever if no timeout is given */
-  if (NULL == timeout)
-    {
-      sos_disable_IRQs(flags);
-      retval = _switch_to_next_thread(BLOCK_MYSELF);
-      sos_restore_IRQs(flags);
-
-      return retval;
-    }
-
-  /* Initialize the timeout action */
-  sos_time_init_action(& timeout_action);
-
-  /* Prepare parameters used by the sleep timeout callback */
-  sleep_timeout_params.thread_to_wakeup 
-    = (struct sos_kthread*)current_kthread;
-  sleep_timeout_params.timeout_triggered = FALSE;
-
-  sos_disable_IRQs(flags);
-
-  /* Now program the timeout ! */
-  SOS_ASSERT_FATAL(SOS_OK ==
-		   sos_time_register_action_relative(& timeout_action,
-						     timeout,
-						     sleep_timeout,
-						     & sleep_timeout_params));
-
-  /* Prepare to block: wait for sleep_timeout() to wakeup us in the
-     timeout kwaitq, or for someone to wake us up in any other
-     waitq */
-  retval = _switch_to_next_thread(BLOCK_MYSELF);
-  /* Unblocked by something ! */
-
-  /* Unblocked by timeout ? */
-  if (sleep_timeout_params.timeout_triggered)
-    {
-      /* Yes */
-      SOS_ASSERT_FATAL(sos_time_is_zero(& timeout_action.timeout));
-      retval = SOS_OK;
-    }
-  else
-    {
-      /* No: We have probably been woken up while in some other
-	 kwaitq */
-      SOS_ASSERT_FATAL(SOS_OK == sos_time_unregister_action(& timeout_action));
-      retval = -SOS_EINTR;
-    }
-
-  sos_restore_IRQs(flags);
-
-  /* Update the remaining timeout */
-  memcpy(timeout, & timeout_action.timeout, sizeof(struct sos_time));
-
-  return retval;
-}
-
-
-sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread)
-{
-  sos_ret_t retval;
-  sos_ui32_t flags;
-
-  if (! kthread)
-    return -SOS_EINVAL;
-  
-  sos_disable_IRQs(flags);
-
-  /* Thread already woken up ? */
-  retval = SOS_OK;
-  switch(sos_kthread_get_state(kthread))
-    {
-    case SOS_KTHR_RUNNING:
-    case SOS_KTHR_READY:
-      /* Do nothing */
-      break;
-
-    case SOS_KTHR_ZOMBIE:
-      retval = -SOS_EFATAL;
-      break;
-
-    default:
-      retval = sos_sched_set_ready(kthread);
-      break;
-    }
-
-  sos_restore_IRQs(flags);
-
-  return retval;
-}
diff -ruN /tmp/sos-code-article6.75/sos/kthread.h ../sos-code-article7/sos/kthread.h
--- /tmp/sos-code-article6.75/sos/kthread.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kthread.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,207 +0,0 @@
-/* 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_KTHREAD_H_
-#define _SOS_KTHREAD_H_
-
-#include <sos/errno.h>
-
-/**
- * @file kthread.h
- *
- * SOS Kernel thread management API
- */
-
-
-/* Forward declaration */
-struct sos_kthread;
-
-#include <hwcore/cpu_context.h>
-#include <sos/sched.h>
-#include <sos/kwaitq.h>
-#include <sos/time.h>
-
-
-/**
- * The possible states of a valid kernel thread
- */
-typedef enum { SOS_KTHR_CREATED, /**< Thread created, not fully initialized */
-	       SOS_KTHR_READY,   /**< Thread fully initialized or waiting
-                                      for CPU after having been blocked */
-	       SOS_KTHR_RUNNING, /**< Thread currently running on CPU */
-	       SOS_KTHR_BLOCKED, /**< Thread waiting for I/O (+ in at LEAST
-				      one kwaitq) and/or sleeping (+ in NO
-				      kwaitq) */
-	       SOS_KTHR_ZOMBIE,  /**< Thread terminated execution, waiting to
-				      be deleted by kernel */
-             } sos_kthread_state_t;
-
-
-/**
- * TCB (Thread Control Block): structure describing a Kernel
- * thread. Don't access these fields directly: prefer using the
- * accessor functions below.
- */
-struct sos_kthread
-{
-#define SOS_KTHR_MAX_NAMELEN 32
-  char name[SOS_KTHR_MAX_NAMELEN];
-
-  sos_kthread_state_t  state;
-  sos_sched_priority_t priority;
-
-  /* The hardware context of the thread */
-  struct sos_cpu_kstate *cpu_kstate;
-  sos_vaddr_t stack_base_addr;
-  sos_size_t  stack_size;
-
-  /* Data specific to each state */
-  union
-  {
-    struct
-    {
-      struct sos_sched_queue *rdy_queue;
-      struct sos_kthread     *rdy_prev, *rdy_next;
-    } ready;
-  }; /* Anonymous union (gcc extenion) */
-
-
-  /*
-   * Data used by the kwaitq subsystem: list of kwaitqueues the thread
-   * is waiting for.
-   *
-   * @note: a RUNNING or READY thread might be in one or more
-   * waitqueues ! The only property we have is that, among these
-   * waitqueues (if any), _at least_ one has woken the thread.
-   */
-  struct sos_kwaitq_entry *kwaitq_list;
-
-
-  /**
-   * Chaining pointers for global ("gbl") list of threads (debug)
-   */
-  struct sos_kthread *gbl_prev, *gbl_next;
-};
-
-
-/**
- * Definition of the function executed by a kernel thread
- */
-typedef void (*sos_kthread_start_routine_t)(void *arg);
-
-
-/**
- * Initialize the subsystem responsible for kernel thread management
- *
- * Initialize primary kernel thread so that it can be handled the same
- * way as an ordinary thread created by sos_kthread_create().
- */
-sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
-				      sos_size_t init_thread_stack_size);
-
-
-/**
- * Create a new kernel thread
- */
-struct sos_kthread *sos_kthread_create(const char *name,
-				       sos_kthread_start_routine_t start_func,
-				       void *start_arg,
-				       sos_sched_priority_t priority);
-
-
-/**
- * Terminate the execution of the current thread. Called by default
- * when the start routine returns.
- */
-void sos_kthread_exit() __attribute__((noreturn));
-
-
-/**
- * Get the identifier of the thread currently running on CPU. Trivial
- * function.
- */
-struct sos_kthread *sos_kthread_get_current();
-
-
-/**
- * If thr == NULL, set the priority of the current thread. Trivial
- * function.
- *
- * @note NOT protected against interrupts
- */
-sos_sched_priority_t sos_kthread_get_priority(struct sos_kthread *thr);
-
-
-/**
- * If thr == NULL, get the state of the current thread. Trivial
- * function.
- *
- * @note NOT protected against interrupts
- */
-sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr);
-
-
-/**
- * If thr == NULL, set the priority of the current thread
- *
- * @note NO context-switch ever occurs in this function !
- */
-sos_ret_t sos_kthread_set_priority(struct sos_kthread *thr,
-				   sos_sched_priority_t priority);
-
-
-/**
- * Yield CPU to another ready thread.
- *
- * @note This is a BLOCKING FUNCTION
- */
-sos_ret_t sos_kthread_yield();
-
-
-/**
- * Release the CPU for (at least) the given delay.
- *
- * @param delay The delay to wait for. If delay == NULL then wait
- * forever that any event occurs.
- *
- * @return SOS_OK when delay expired (and delay is reset to zero),
- * -SOS_EINTR otherwise (and delay contains the amount of time
- * remaining).
- *
- * @note This is a BLOCKING FUNCTION
- */
-sos_ret_t sos_kthread_sleep(/* in/out */struct sos_time *delay);
-
-
-/**
- * Mark the given thread as READY (if not already ready) even if it is
- * blocked in a kwaitq or in a sleep ! As a result, the interrupted
- * kwaitq/sleep function call of the thread will return with
- * -SOS_EINTR.
- *
- * @return -SOS_EINVAL if thread does not exist, or -SOS_EFATAL if
- * marked ZOMBIE.
- *
- * @note As a result, the semaphore/mutex/conditions/... functions
- * return values SHOULD ALWAYS be checked ! If they are != SOS_OK,
- * then the caller should consider that the resource is not aquired
- * because somebody woke the thread by some way.
- */
-sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread);
-
-
-#endif /* _SOS_KTHREAD_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/kwaitq.c ../sos-code-article7/sos/kwaitq.c
--- /tmp/sos-code-article6.75/sos/kwaitq.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kwaitq.c	2005-04-27 20:14:21.000000000 +0200
@@ -77,7 +77,7 @@
 sos_ret_t sos_kwaitq_init_entry(struct sos_kwaitq_entry *kwq_entry)
 {
   memset(kwq_entry, 0x0, sizeof(struct sos_kwaitq_entry));
-  kwq_entry->kthread = sos_kthread_get_current();
+  kwq_entry->thread = sos_thread_get_current();
   return SOS_OK;
 }
 
@@ -96,7 +96,7 @@
   SOS_ASSERT_FATAL(NULL == kwq_entry->kwaitq);
 
   /* sos_kwaitq_init_entry() has not been called ?! */
-  SOS_ASSERT_FATAL(NULL != kwq_entry->kthread);
+  SOS_ASSERT_FATAL(NULL != kwq_entry->thread);
 
   /* (Re-)Initialize wakeup status of the entry */
   kwq_entry->wakeup_triggered = FALSE;
@@ -125,7 +125,7 @@
 	    /* Does the thread we want to insert have higher priority than
 	       the given thread in the queue ? */
 	    if (SOS_SCHED_PRIO_CMP(prio,
-				   sos_kthread_get_priority(entry->kthread))
+				   sos_thread_get_priority(entry->thread))
 		> 0)
 	      {
 		/* Yes: we insert before this given thread */
@@ -157,8 +157,8 @@
     }
 
   /* Update the list of waitqueues for the thread */
-  list_add_tail_named(kwq_entry->kthread->kwaitq_list, kwq_entry,
-		      prev_entry_for_kthread, next_entry_for_kthread);
+  list_add_tail_named(kwq_entry->thread->kwaitq_list, kwq_entry,
+		      prev_entry_for_thread, next_entry_for_thread);
 
   kwq_entry->kwaitq = kwq;
   
@@ -174,7 +174,7 @@
 
   sos_disable_IRQs(flags);
   retval = _kwaitq_add_entry(kwq, kwq_entry,
-		             sos_kthread_get_priority(kwq_entry->kthread));
+		             sos_thread_get_priority(kwq_entry->thread));
   sos_restore_IRQs(flags);
 
   return retval;
@@ -192,8 +192,8 @@
   list_delete_named(kwq->waiting_list, kwq_entry,
 		    prev_entry_in_kwaitq, next_entry_in_kwaitq);
 
-  list_delete_named(kwq_entry->kthread->kwaitq_list, kwq_entry,
-		    prev_entry_for_kthread, next_entry_for_kthread);
+  list_delete_named(kwq_entry->thread->kwaitq_list, kwq_entry,
+		    prev_entry_for_thread, next_entry_for_thread);
 
   kwq_entry->kwaitq = NULL;
   return SOS_OK;
@@ -226,10 +226,10 @@
   sos_disable_IRQs(flags);
 
   retval = _kwaitq_add_entry(kwq, & kwq_entry,
-		             sos_kthread_get_priority(kwq_entry.kthread));
+		             sos_thread_get_priority(kwq_entry.thread));
 
   /* Wait for wakeup or timeout */
-  sos_kthread_sleep(timeout);
+  sos_thread_sleep(timeout);
   /* Woken up ! */
 
   /* Sleep delay elapsed ? */
@@ -253,7 +253,7 @@
 
 
 sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
-			    unsigned int nb_kthreads,
+			    unsigned int nb_threads,
 			    sos_ret_t wakeup_status)
 {
   sos_ui32_t flags;
@@ -261,7 +261,7 @@
   sos_disable_IRQs(flags);
 
   /* Wake up as much threads waiting in waitqueue as possible (up to
-     nb_kthreads), scanning the list in FIFO/decreasing priority order
+     nb_threads), scanning the list in FIFO/decreasing priority order
      (depends on the kwaitq ordering) */
   while (! list_is_empty_named(kwq->waiting_list,
 			       prev_entry_in_kwaitq, next_entry_in_kwaitq))
@@ -270,8 +270,8 @@
 	= list_get_head_named(kwq->waiting_list,
 			      prev_entry_in_kwaitq, next_entry_in_kwaitq);
 
-      /* Enough kthreads woken up ? */
-      if (nb_kthreads <= 0)
+      /* Enough threads woken up ? */
+      if (nb_threads <= 0)
 	break;
 
       /*
@@ -279,7 +279,7 @@
        */
 
       /* Thread already woken up ? */
-      if (SOS_KTHR_RUNNING == sos_kthread_get_state(kwq_entry->kthread))
+      if (SOS_THR_RUNNING == sos_thread_get_state(kwq_entry->thread))
 	{
 	  /* Yes => Do nothing because WE are that woken-up thread. In
 	     particular: don't call set_ready() here because this
@@ -290,7 +290,7 @@
       else
 	{
 	  /* No => wake it up now. */
-	  sos_sched_set_ready(kwq_entry->kthread);
+	  sos_sched_set_ready(kwq_entry->thread);
 	}
 
       /* Remove this waitq entry */
@@ -299,7 +299,7 @@
       kwq_entry->wakeup_status    = wakeup_status;
 
       /* Next iteration... */
-      nb_kthreads --;
+      nb_threads --;
     }
 
   sos_restore_IRQs(flags);
@@ -308,7 +308,7 @@
 }
 
 
-/* Internal function (callback for kthread subsystem) */
+/* Internal function (callback for thread subsystem) */
 sos_ret_t sos_kwaitq_change_priority(struct sos_kwaitq *kwq,
 				     struct sos_kwaitq_entry *kwq_entry,
 				     sos_sched_priority_t priority)
diff -ruN /tmp/sos-code-article6.75/sos/kwaitq.h ../sos-code-article7/sos/kwaitq.h
--- /tmp/sos-code-article6.75/sos/kwaitq.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/kwaitq.h	2005-04-27 20:14:21.000000000 +0200
@@ -19,7 +19,7 @@
 #define _SOS_KWAITQ_H_
 
 #include <sos/errno.h>
-#include <sos/kthread.h>
+#include <sos/thread.h>
 #include <sos/time.h>
 
 
@@ -28,7 +28,7 @@
  *
  * Low-level functions to manage queues of threads waiting for a
  * resource. These functions are public, except
- * sos_kwaitq_change_priority() that is a callback for the kthread
+ * sos_kwaitq_change_priority() that is a callback for the thread
  * subsystem. However, for higher-level synchronization primitives
  * such as mutex, semaphores, conditions, ... prefer to look at the
  * corresponding libraries.
@@ -87,7 +87,7 @@
 struct sos_kwaitq_entry
 {
   /** The thread associted with this entry */
-  struct sos_kthread *kthread;
+  struct sos_thread *thread;
 
   /** The kwaitqueue this entry belongs to */
   struct sos_kwaitq *kwaitq;
@@ -103,7 +103,7 @@
   struct sos_kwaitq_entry *prev_entry_in_kwaitq, *next_entry_in_kwaitq;
 
   /** Other entries for the thread */
-  struct sos_kwaitq_entry *prev_entry_for_kthread, *next_entry_for_kthread;  
+  struct sos_kwaitq_entry *prev_entry_for_thread, *next_entry_for_thread;  
 };
 
 
@@ -134,7 +134,7 @@
 
 /**
  * Initialize a waitqueue entry. Mainly consists in updating the
- * "kthread" field of the entry (set to current running thread), and
+ * "thread" field of the entry (set to current running thread), and
  * initializing the remaining of the entry as to indicate it does not
  * belong to any waitq.
  */
@@ -189,7 +189,7 @@
 
 
 /**
- * Wake up as much as nb_kthread threads (SOS_KWQ_WAKEUP_ALL to wake
+ * Wake up as much as nb_thread threads (SOS_KWQ_WAKEUP_ALL to wake
  * up all threads) in the kwaitq kwq, in FIFO or decreasing priority
  * order (depends on the ordering scheme selected at kwaitq
  * initialization time).
@@ -198,7 +198,7 @@
  * the thread will effectively woken up due to this wakeup.
  */
 sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
-			    unsigned int nb_kthreads,
+			    unsigned int nb_threads,
 			    sos_ret_t wakeup_status);
 #define SOS_KWQ_WAKEUP_ALL (~((unsigned int)0))
 
@@ -206,8 +206,8 @@
 /**
  * @note INTERNAL function (in particular: interrupts not disabled) !
  *
- * @note: The use of this function is RESERVED (to kthread.c). Do not
- * call it directly: use sos_kthread_set_priority() for that !
+ * @note: The use of this function is RESERVED (to thread.c). Do not
+ * call it directly: use sos_thread_set_priority() for that !
  */
 sos_ret_t sos_kwaitq_change_priority(struct sos_kwaitq *kwq,
 				     struct sos_kwaitq_entry *kwq_entry,
diff -ruN /tmp/sos-code-article6.75/sos/list.h ../sos-code-article7/sos/list.h
--- /tmp/sos-code-article6.75/sos/list.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/list.h	2005-04-27 20:14:20.000000000 +0200
@@ -41,6 +41,9 @@
 #define list_is_empty_named(list,prev,next) \
   ((list) == NULL)
 
+#define list_is_singleton_named(list,prev,next) \
+  ( ((list) != NULL) && ((list)->prev == (list)->next) && ((list) == (list)->next) )
+
 #define list_get_head_named(list,prev,next) \
   (list)
 
@@ -124,7 +127,9 @@
     ((list) != (iterator)) || \
     ( ((list) == (iterator)) && (nb_elements == 0)) ))
 
-/** Loop statement that also removes the item at each iteration */
+/** Loop statement that also removes the item at each iteration. The
+    body of the loop is allowed to delete the iterator element from
+    memory. */
 #define list_collapse_named(list,iterator,prev,next) \
         for ( ; ({ ((iterator) = (list)) ; \
                    if (list) list_delete_named(list,iterator,prev,next) ; \
@@ -145,6 +150,9 @@
 #define list_is_empty(list) \
   list_is_empty_named(list,prev,next)
 
+#define list_is_singleton(list) \
+  list_is_singleton_named(list,prev,next)
+
 #define list_get_head(list) \
   list_get_head_named(list,prev,next) \
 
diff -ruN /tmp/sos-code-article6.75/sos/macros.h ../sos-code-article7/sos/macros.h
--- /tmp/sos-code-article6.75/sos/macros.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/macros.h	2005-04-27 20:14:20.000000000 +0200
@@ -38,4 +38,10 @@
 #define SOS_IS_POWER_OF_2(val) \
   ((((val) - 1) & (val)) == 0)
 
+/**
+ * Standard macro to get the offset of the given field in a structure
+ */
+#define offsetof(type,field) \
+  ((unsigned) &(((type *)0)->field))
+
 #endif /* _SOS_MACROS_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/main.c ../sos-code-article7/sos/main.c
--- /tmp/sos-code-article6.75/sos/main.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/main.c	2005-04-27 20:14:20.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The SOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -27,14 +26,18 @@
 #include <sos/list.h>
 #include <sos/physmem.h>
 #include <hwcore/paging.h>
+#include <hwcore/mm_context.h>
+#include <hwcore/swintr.h>
 #include <sos/kmem_vmm.h>
 #include <sos/kmalloc.h>
 #include <sos/time.h>
-#include <sos/kthread.h>
+#include <sos/thread.h>
+#include <sos/process.h>
 #include <sos/klibc.h>
 #include <sos/assert.h>
 #include <drivers/x86_videomem.h>
 #include <drivers/bochs.h>
+#include <sos/calcload.h>
 
 
 /* Helper function to display each bits of a 32bits integer on the
@@ -59,8 +62,7 @@
 
 
 /* Clock IRQ handler */
-static void clk_it(int intid,
-		   const struct sos_cpu_kstate *cpu_kstate)
+static void clk_it(int intid)
 {
   static sos_ui32_t clock_count = 0;
 
@@ -71,6 +73,9 @@
 
   /* Execute the expired timeout actions (if any) */
   sos_time_do_tick();
+
+  /* Update scheduler statistics and status */
+  sos_sched_do_timer_tick();
 }
 
 
@@ -78,89 +83,41 @@
  * Page fault exception handling
  */
 
-/* Helper function to dump a backtrace on bochs and/or the console */
-static void dump_backtrace(const struct sos_cpu_kstate *cpu_kstate,
-			   sos_vaddr_t stack_bottom,
-			   sos_size_t  stack_size,
-			   sos_bool_t on_console,
-			   sos_bool_t on_bochs)
-{
-  static void backtracer(sos_vaddr_t PC,
-			 sos_vaddr_t params,
-			 sos_ui32_t depth,
-			 void *custom_arg)
-    {
-      sos_ui32_t invalid = 0xffffffff, *arg1, *arg2, *arg3, *arg4;
-
-      /* Get the address of the first 3 arguments from the
-	 frame. Among these arguments, 0, 1, 2, 3 arguments might be
-	 meaningful (depending on how many arguments the function may
-	 take). */
-      arg1 = (sos_ui32_t*)params;
-      arg2 = (sos_ui32_t*)(params+4);
-      arg3 = (sos_ui32_t*)(params+8);
-      arg4 = (sos_ui32_t*)(params+12);
-
-      /* Make sure the addresses of these arguments fit inside the
-	 stack boundaries */
-#define INTERVAL_OK(b,v,u) ( ((b) <= (sos_vaddr_t)(v)) \
-                             && ((sos_vaddr_t)(v) < (u)) )
-      if (!INTERVAL_OK(stack_bottom, arg1, stack_bottom + stack_size))
-	arg1 = &invalid;
-      if (!INTERVAL_OK(stack_bottom, arg2, stack_bottom + stack_size))
-	arg2 = &invalid;
-      if (!INTERVAL_OK(stack_bottom, arg3, stack_bottom + stack_size))
-	arg3 = &invalid;
-      if (!INTERVAL_OK(stack_bottom, arg4, stack_bottom + stack_size))
-	arg4 = &invalid;
-
-      /* Print the function context for this frame */
-      if (on_bochs)
-	sos_bochs_printf("[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x\n",
-			 (unsigned)depth, (unsigned)PC,
-			 (unsigned)*arg1, (unsigned)*arg2,
-			 (unsigned)*arg3);
-
-      if (on_console)
-	sos_x86_videomem_printf(23-depth, 3,
-				SOS_X86_VIDEO_BG_BLUE
-				  | SOS_X86_VIDEO_FG_LTGREEN,
-				"[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x arg4=0x%x",
-				(unsigned)depth, PC,
-				(unsigned)*arg1, (unsigned)*arg2,
-				(unsigned)*arg3, (unsigned)*arg4);
-      
-    }
-
-  sos_backtrace(cpu_kstate, 15, stack_bottom, stack_size, backtracer, NULL);
-}
-
 
 /* Page fault exception handler with demand paging for the kernel */
-static void pgflt_ex(int intid, const struct sos_cpu_kstate *ctxt)
+static void pgflt_ex(int intid, struct sos_cpu_state *ctxt)
 {
   static sos_ui32_t demand_paging_count = 0;
-  sos_vaddr_t faulting_vaddr = sos_cpu_kstate_get_EX_faulting_vaddr(ctxt);
+  sos_vaddr_t faulting_vaddr  = sos_cpu_context_get_EX_faulting_vaddr(ctxt);
   sos_paddr_t ppage_paddr;
 
+  if (sos_cpu_context_is_in_user_mode(ctxt))
+    {
+      /* User-mode page faults are considered unresolved for the
+	 moment */
+      sos_bochs_printf("Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\n",
+		       sos_cpu_context_get_PC(ctxt),
+		       (unsigned)faulting_vaddr,
+		       (unsigned)sos_cpu_context_get_EX_info(ctxt));
+      sos_bochs_printf("Terminating User thread\n");
+      sos_thread_exit();
+    }
+
   /* Check if address is covered by any VMM range */
   if (! sos_kmem_vmm_is_valid_vaddr(faulting_vaddr))
     {
       /* No: The page fault is out of any kernel virtual region. For
 	 the moment, we don't handle this. */
-      dump_backtrace(ctxt,
-		     bootstrap_stack_bottom,
-		     bootstrap_stack_size,
-		     TRUE, TRUE);
-      sos_display_fatal_error("Unresolved page Fault on access to address 0x%x (info=%x)!",
+      sos_display_fatal_error("Unresolved page Fault at instruction 0x%x on access to address 0x%x (info=%x)!",
+			      sos_cpu_context_get_PC(ctxt),
 			      (unsigned)faulting_vaddr,
-			      (unsigned)sos_cpu_kstate_get_EX_info(ctxt));
+			      (unsigned)sos_cpu_context_get_EX_info(ctxt));
       SOS_ASSERT_FATAL(! "Got page fault (note: demand paging is disabled)");
     }
 
 
   /*
-   * Demand paging
+   * Demand paging in kernel space
    */
  
   /* Update the number of demand paging requests handled */
@@ -220,7 +177,7 @@
 	{
 	  sos_bochs_printf("[37myield(%c)[m\n", thr_arg->character);
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'Y');
-	  SOS_ASSERT_FATAL(SOS_OK == sos_kthread_yield());
+	  SOS_ASSERT_FATAL(SOS_OK == sos_thread_yield());
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R');
 	}
 
@@ -230,7 +187,7 @@
 	  struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 };
 	  sos_bochs_printf("[37msleep1(%c)[m\n", thr_arg->character);
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's');
-	  SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t));
+	  SOS_ASSERT_FATAL(SOS_OK == sos_thread_sleep(& t));
 	  SOS_ASSERT_FATAL(sos_time_is_zero(& t));
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R');
 	}
@@ -241,7 +198,7 @@
 	  struct sos_time t = (struct sos_time){ .sec=0, .nanosec=300000000 };
 	  sos_bochs_printf("[37msleep2(%c)[m\n", thr_arg->character);
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'S');
-	  SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t));
+	  SOS_ASSERT_FATAL(SOS_OK == sos_thread_sleep(& t));
 	  SOS_ASSERT_FATAL(sos_time_is_zero(& t));
 	  sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R');
 	}
@@ -251,7 +208,7 @@
 }
 
 
-static void test_kthread()
+static void test_thread()
 {
   /* "static" variables because we want them to remain even when the
      function returns */
@@ -261,22 +218,22 @@
   sos_disable_IRQs(flags);
 
   arg_b = (struct thr_arg) { .character='b', .col=0, .row=21, .color=0x14 };
-  sos_kthread_create("YO[b]", demo_thread, (void*)&arg_b, SOS_SCHED_PRIO_TS_LOWEST);
+  sos_create_kernel_thread("YO[b]", demo_thread, (void*)&arg_b, SOS_SCHED_PRIO_TS_LOWEST);
 
   arg_c = (struct thr_arg) { .character='c', .col=46, .row=21, .color=0x14 };
-  sos_kthread_create("YO[c]", demo_thread, (void*)&arg_c, SOS_SCHED_PRIO_TS_LOWEST);
+  sos_create_kernel_thread("YO[c]", demo_thread, (void*)&arg_c, SOS_SCHED_PRIO_TS_LOWEST);
 
   arg_d = (struct thr_arg) { .character='d', .col=0, .row=20, .color=0x14 };
-  sos_kthread_create("YO[d]", demo_thread, (void*)&arg_d, SOS_SCHED_PRIO_TS_LOWEST-1);
+  sos_create_kernel_thread("YO[d]", demo_thread, (void*)&arg_d, SOS_SCHED_PRIO_TS_LOWEST-1);
 
   arg_e = (struct thr_arg) { .character='e', .col=0, .row=19, .color=0x14 };
-  sos_kthread_create("YO[e]", demo_thread, (void*)&arg_e, SOS_SCHED_PRIO_TS_LOWEST-2);
+  sos_create_kernel_thread("YO[e]", demo_thread, (void*)&arg_e, SOS_SCHED_PRIO_TS_LOWEST-2);
 
   arg_R = (struct thr_arg) { .character='R', .col=0, .row=17, .color=0x1c };
-  sos_kthread_create("YO[R]", demo_thread, (void*)&arg_R, SOS_SCHED_PRIO_RT_LOWEST);
+  sos_create_kernel_thread("YO[R]", demo_thread, (void*)&arg_R, SOS_SCHED_PRIO_RT_LOWEST);
 
   arg_S = (struct thr_arg) { .character='S', .col=0, .row=16, .color=0x1c };
-  sos_kthread_create("YO[S]", demo_thread, (void*)&arg_S, SOS_SCHED_PRIO_RT_LOWEST-1);
+  sos_create_kernel_thread("YO[S]", demo_thread, (void*)&arg_S, SOS_SCHED_PRIO_RT_LOWEST-1);
 
   sos_restore_IRQs(flags);
 }
@@ -286,7 +243,7 @@
  * An operating system MUST always have a ready thread ! Otherwise:
  * what would the CPU have to execute ?!
  */
-static void idle_kthread()
+static void idle_thread()
 {
   sos_ui32_t idle_twiddle = 0;
 
@@ -301,7 +258,67 @@
 		   idle_twiddle);
       
       /* Lend the CPU to some other thread */
-      sos_kthread_yield();
+      sos_thread_yield();
+    }
+}
+
+
+/* ======================================================================
+ * Kernel thread showing some CPU usage statistics on the console every 1s
+ */
+static void stat_thread()
+{
+  while (1)
+    {
+      sos_ui32_t flags;
+      sos_ui32_t load1, load5, load15;
+      char str1[11], str5[11], str15[11];
+      struct sos_time t;
+      t.sec = 1;
+      t.nanosec = 0;
+
+      sos_thread_sleep(& t);
+
+      sos_disable_IRQs(flags);
+
+      /* The IDLE task is EXcluded in the following computation */
+      sos_load_get_sload(&load1, &load5, &load15);
+      sos_load_to_string(str1, load1);
+      sos_load_to_string(str5, load5);
+      sos_load_to_string(str15, load15);
+      sos_x86_videomem_printf(16, 34,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "Kernel (- Idle): %s %s %s    ",
+			      str1, str5, str15);
+
+      sos_load_get_uload(&load1, &load5, &load15);
+      sos_load_to_string(str1, load1);
+      sos_load_to_string(str5, load5);
+      sos_load_to_string(str15, load15);
+      sos_x86_videomem_printf(17, 34,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "User: %s %s %s        ",
+			      str1, str5, str15);
+
+      sos_load_get_uratio(&load1, &load5, &load15);
+      sos_load_to_string(str1, load1);
+      sos_load_to_string(str5, load5);
+      sos_load_to_string(str15, load15);
+      sos_x86_videomem_printf(18, 34,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "User CPU %%: %s %s %s        ",
+			      str1, str5, str15);
+
+      /* The IDLE task is INcluded in the following computation */
+      sos_load_get_sratio(&load1, &load5, &load15);
+      sos_load_to_string(str1, load1);
+      sos_load_to_string(str5, load5);
+      sos_load_to_string(str15, load15);
+      sos_x86_videomem_printf(19, 34,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "Kernel CPU %% (+ Idle): %s %s %s    ",
+			      str1, str5, str15);
+      sos_restore_IRQs(flags);
     }
 }
 
@@ -333,16 +350,16 @@
     sos_x86_videomem_printf(1, 0,
 			    SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
 			    "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",
-			    "SOS", ',',
+			    "SOS article 7", ',',
 			    (unsigned)(mbi->mem_upper >> 10) + 1,
 			    (unsigned)mbi->mem_upper);
   else
     /* Not loaded with grub */
     sos_x86_videomem_printf(1, 0,
 			    SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
-			    "Welcome to SOS");
+			    "Welcome to SOS article 7");
 
-  sos_bochs_putstring("Message in a bochs\n");
+  sos_bochs_putstring("Message in a bochs: This is SOS article 7.\n");
 
   /* Setup CPU segmentation and IRQ subsystem */
   sos_gdt_subsystem_setup();
@@ -408,7 +425,7 @@
 
   /*
    * Setup kernel virtual memory allocator
-   */ 
+   */
 
   if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr,
 				   sos_kernel_core_top_paddr,
@@ -421,55 +438,77 @@
     sos_bochs_printf("Could not setup the Kmalloc subsystem\n");
 
   /*
+   * Initialize the MMU context subsystem
+   */
+  sos_mm_context_subsystem_setup();
+
+  /*
+   * Initialize the CPU context subsystem
+   */
+  sos_cpu_context_subsystem_setup();
+
+  /*
+   * Bind the syscall handler to its software interrupt handler
+   */
+  sos_swintr_subsystem_setup();
+
+
+  /*
    * Initialize the Kernel thread and scheduler subsystems
    */
   
   /* Initialize kernel thread subsystem */
-  sos_kthread_subsystem_setup(bootstrap_stack_bottom,
-			      bootstrap_stack_size);
+  sos_thread_subsystem_setup(bootstrap_stack_bottom,
+			     bootstrap_stack_size);
 
   /* Initialize the scheduler */
   sos_sched_subsystem_setup();
 
   /* Declare the IDLE thread */
-  SOS_ASSERT_FATAL(sos_kthread_create("idle", idle_kthread, NULL,
-				      SOS_SCHED_PRIO_TS_LOWEST) != NULL);
+  SOS_ASSERT_FATAL(sos_create_kernel_thread("idle", idle_thread, NULL,
+   					    SOS_SCHED_PRIO_TS_LOWEST) != NULL);
 
+  /* Prepare the stats subsystem */
+  sos_load_subsystem_setup();
+
+  /* Declare a thread that prints some stats */
+  SOS_ASSERT_FATAL(sos_create_kernel_thread("stat_thread", stat_thread,
+					    NULL,
+					    SOS_SCHED_PRIO_TS_LOWEST) != NULL);
 
-  /* Enabling the HW interrupts here, this will make the timer HW
-     interrupt call the scheduler */
-  asm volatile ("sti\n");
 
   /*
-   * Force the idle thread to run at least once to force a context
-   * switch. This way the "cpu_kstate" of the kernel thread for the
-   * sos_main thread gets a chance to be filled with the current CPU
-   * context. Useful only if we call sos_kthread_exit() too early from
-   * sos_main: a "stack overflow" will be wrongly detected simply
-   * because the "cpu_kstate" of the thread has not be correctly
-   * initialised. A context switch is a good way to initialise it.
+   * Initialize process stuff
    */
-  sos_kthread_yield();
+  sos_process_subsystem_setup();
+
+
+  /* Enabling the HW interrupts here, this will make the timer HW
+     interrupt call the scheduler */
+  asm volatile ("sti\n");
 
+  /* Run some tests involving USER processes and threads */
+  extern void test_art7();
+  test_art7();
 
   /* Now run some Kernel threads just for fun ! */
   extern void MouseSim();
-  MouseSim(); 
-  test_kthread();
+  MouseSim();
+  test_thread();
 
   /*
    * We can safely exit from this function now, for there is already
    * an idle Kernel thread ready to make the CPU busy working...
    *
-   * However, we must EXPLICITELY call sos_kthread_exit() because a
+   * However, we must EXPLICITELY call sos_thread_exit() because a
    * simple "return" will return nowhere ! Actually this first thread
    * was initialized by the Grub bootstrap stage, at a time when the
    * word "thread" did not exist. This means that the stack was not
-   * setup in order for a return here to call sos_kthread_exit()
+   * setup in order for a return here to call sos_thread_exit()
    * automagically. Hence we must call it manually. This is the ONLY
    * kernel thread where we must do this manually.
    */
   sos_bochs_printf("Bye from primary thread !\n");
-  sos_kthread_exit();
+  sos_thread_exit();
   SOS_FATAL_ERROR("No trespassing !");
 }
diff -ruN /tmp/sos-code-article6.75/sos/mouse_sim.c ../sos-code-article7/sos/mouse_sim.c
--- /tmp/sos-code-article6.75/sos/mouse_sim.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/mouse_sim.c	2005-04-27 20:14:21.000000000 +0200
@@ -27,7 +27,7 @@
 
 #include <sos/assert.h>
 #include <sos/klibc.h>
-#include <sos/kthread.h>
+#include <sos/thread.h>
 #include <sos/ksynch.h>
 #include <sos/kmalloc.h>
 #include <drivers/x86_videomem.h>
@@ -83,7 +83,7 @@
 	sos_ui32_t	        Status;
 	Color_t		        Color;//Couleur de l'element
 	Point_t		        P;//Coordonnees de l'element
-	struct sos_kthread *	ThreadID;//Thread associe a la souris
+	struct sos_thread *	ThreadID;//Thread associe a la souris
 	int		        Way;//Direction de la souris
 	};
 
@@ -92,7 +92,6 @@
 //*****************************************************************************
 // Prototypes des fonctions/procedures :
 //*****************************************************************************
-static void MouseCommander(void);
 static void DrawMap(void);
 static sos_ret_t CreateMap(void);
 static sos_ret_t InitMapInput(Element_t * * pMap);
@@ -128,10 +127,6 @@
 }
 
 
-void ThreadDelete(struct sos_kthread *tid)
-{
-  SOS_ASSERT_FATAL(! "TODO !");
-}
 
 //*****************************************************************************
 // Point d'entre de la 'simulation'
@@ -150,9 +145,9 @@
   SOS_ASSERT_FATAL(SOS_OK == CreateMap());
 
   //Creation du thread createur de souris
-  SOS_ASSERT_FATAL(sos_kthread_create("MouseCreator",
-				      (sos_kthread_start_routine_t)MouseCreator,
-				      0, SOS_SCHED_PRIO_TS_LOWEST-1) != NULL);
+  SOS_ASSERT_FATAL(sos_create_kernel_thread("MouseCreator",
+					    (sos_kernel_thread_start_routine_t)MouseCreator,
+					    0, SOS_SCHED_PRIO_TS_LOWEST-1) != NULL);
 
 }
 
@@ -380,7 +375,7 @@
 	  delay_ms = MOUSE_SPEED_MIN + (random() % MouseSpeed);
 	  delay.sec     = delay_ms / 1000;
 	  delay.nanosec = (delay_ms % 1000) * 1000000;
-	  sos_kthread_sleep(& delay);
+	  sos_thread_sleep(& delay);
 	}
 
 	// Libere la structure associee
@@ -780,11 +775,15 @@
 					pElement->Color = SOS_X86_VIDEO_FG_LTRED;
 					pElement->P = p;
 					pElement->Way = 0;
-					pElement->ThreadID = sos_kthread_create("Mouse", (sos_kthread_start_routine_t)Mouse, pElement, SOS_SCHED_PRIO_TS_LOWEST-1);
+					pElement->ThreadID
+					  = sos_create_kernel_thread("Mouse",
+								     (sos_kernel_thread_start_routine_t)Mouse,
+								     pElement, SOS_SCHED_PRIO_TS_LOWEST-1);
 					if(pElement->ThreadID == 0)
 					{
 						sos_kfree((sos_vaddr_t)pElement);
 						pElement = NULL;
+						return -SOS_ENOMEM;
 					}
 					pMap[p.X + (p.Y * MAP_X)] = pElement;
 					MouseCount++;
diff -ruN /tmp/sos-code-article6.75/sos/physmem.c ../sos-code-article7/sos/physmem.c
--- /tmp/sos-code-article6.75/sos/physmem.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/physmem.c	2005-04-27 20:14:21.000000000 +0200
@@ -1,4 +1,5 @@
 /* Copyright (C) 2004  David Decotigny
+   Copyright (C) 2000  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
@@ -29,13 +30,25 @@
   sos_paddr_t paddr;
 
   /** The reference count for this physical page. > 0 means that the
-     page is in the used list. */
+     page is in the nonfree list. */
   sos_count_t ref_cnt;
 
+  /**
+   * An additional counter for user-defined use. The management of this
+   * counter is up to the user, however a simple set of rules applies:
+   *   - it can only be incremented/decremented if the page is referenced
+   *   - when it reaches 0, no automatic action is taken
+   * The first rule means in particular that a page whose reference
+   * count reaches 0 (=> will be freed) cannot have a custom_count
+   * value > 0 ! The system will be HALTED if this ever happens
+   */
+  sos_count_t occupation_cnt;
+
+
   /** Some data associated with the page when it is mapped in kernel space */
   struct sos_kmem_range *kernel_range;
 
-  /** The other pages on the list (used, free) */
+  /** The other pages on the list (nonfree, free) */
   struct physical_page_descr *prev, *next;
 };
 
@@ -50,14 +63,14 @@
 /** The list of physical pages currently available */
 static struct physical_page_descr *free_ppage;
 
-/** The list of physical pages currently in use */
-static struct physical_page_descr *used_ppage;
+/** The list of physical pages currently allocated */
+static struct physical_page_descr *nonfree_ppage;
 
 /** We will store here the interval of valid physical addresses */
 static sos_paddr_t physmem_base, physmem_top;
 
-/** We store the number of pages used/free */
-static sos_count_t physmem_total_pages, physmem_used_pages;
+/** We store the number of pages nonfree/free */
+static sos_count_t physmem_total_pages, physmem_nonfree_pages;
 
 sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size,
 				      /* out */sos_paddr_t *kernel_core_base,
@@ -72,9 +85,9 @@
   /* Make sure ram size is aligned on a page boundary */
   ram_size = SOS_PAGE_ALIGN_INF(ram_size);/* Yes, we may lose at most a page */
 
-  /* Reset the used/free page lists before building them */
-  free_ppage = used_ppage = NULL;
-  physmem_total_pages = physmem_used_pages = 0;
+  /* Reset the nonfree/free page lists before building them */
+  free_ppage = nonfree_ppage = NULL;
+  physmem_total_pages = physmem_nonfree_pages = 0;
 
   /* Make sure that there is enough memory to store the array of page
      descriptors */
@@ -138,7 +151,7 @@
       else
 	todo = PPAGE_MARK_FREE;
 
-      /* Actually does the insertion in the used/free page lists */
+      /* Actually does the insertion in the nonfree/free page lists */
       physmem_total_pages ++;
       switch (todo)
 	{
@@ -150,8 +163,8 @@
 	case PPAGE_MARK_KERNEL:
 	case PPAGE_MARK_HWMAP:
 	  ppage_descr->ref_cnt = 1;
-	  list_add_head(used_ppage, ppage_descr);
-	  physmem_used_pages ++;
+	  list_add_head(nonfree_ppage, ppage_descr);
+	  physmem_nonfree_pages ++;
 	  break;
 
 	default:
@@ -174,18 +187,21 @@
   /* Retrieve a page in the free list */
   ppage_descr = list_pop_head(free_ppage);
 
-  /* The page is assumed not to be already used */
+  /* The page is assumed not to be already in the nonfree list */
   SOS_ASSERT_FATAL(ppage_descr->ref_cnt == 0);
 
-  /* Mark the page as used (this of course sets the ref count to 1) */
+  /* Mark the page as nonfree (this of course sets the ref count to 1) */
   ppage_descr->ref_cnt ++;
 
-  /* No associated kernel range by default */
-  ppage_descr->kernel_range = NULL;
+  /* The page descriptor should be unmodified since previous
+     deallocation. Otherwise this means that something unauthorized
+     overwrote the page descriptor table contents ! */
+  SOS_ASSERT_FATAL(ppage_descr->occupation_cnt == 0);
+  SOS_ASSERT_FATAL(ppage_descr->kernel_range == NULL);
   
-  /* Put the page in the used list */
-  list_add_tail(used_ppage, ppage_descr);
-  physmem_used_pages ++;
+  /* Put the page in the nonfree list */
+  list_add_tail(nonfree_ppage, ppage_descr);
+  physmem_nonfree_pages ++;
 
   return ppage_descr->paddr;
 }
@@ -224,21 +240,23 @@
   ppage_descr->ref_cnt ++;
 
   /* If the page is newly referenced (ie we are the only owners of the
-     page => ref cnt == 1), transfer it in the used pages list */
+     page => ref cnt == 1), transfer it in the nonfree pages list */
   if (ppage_descr->ref_cnt == 1)
     {
+      /* The page descriptor should be unmodified since previous
+	 deallocation. Otherwise this means that something unauthorized
+	 overwrote the page descriptor table contents ! */
+      SOS_ASSERT_FATAL(ppage_descr->occupation_cnt == 0);
+      SOS_ASSERT_FATAL(ppage_descr->kernel_range == NULL);
+      
       list_delete(free_ppage, ppage_descr);
-
-      /* No associated kernel range by default */
-      ppage_descr->kernel_range = NULL;
-  
-      list_add_tail(used_ppage, ppage_descr);
-      physmem_used_pages ++;
+      list_add_tail(nonfree_ppage, ppage_descr);
+      physmem_nonfree_pages ++;
 
       /* The page is newly referenced */
       return FALSE;
     }
-
+  
   /* The page was already referenced by someone */
   return TRUE;
 }
@@ -257,31 +275,80 @@
   if (! ppage_descr)
     return -SOS_EINVAL;
 
-  /* Don't do anything if the page is not in the used list */
+  /* Don't do anything if the page is not in the nonfree list */
   if (ppage_descr->ref_cnt <= 0)
     return -SOS_EINVAL;
 
   /* Unreference the page, and, when no mapping is active anymore, put
      the page in the free list */
   ppage_descr->ref_cnt--;
-  if (ppage_descr->ref_cnt <= 0)
+  if (ppage_descr->ref_cnt == 0)
     {
+      /* Make sure that the occupation counter is set to 0 */
+      SOS_ASSERT_FATAL(ppage_descr->occupation_cnt == 0);
+
       /* Reset associated kernel range */
       ppage_descr->kernel_range = NULL;
   
-      /* Transfer the page, considered USED, to the free list */
-      list_delete(used_ppage, ppage_descr);
-      physmem_used_pages --;
+      /* Transfer the page, considered NON-FREE, to the free list */
+      list_delete(nonfree_ppage, ppage_descr);
+      physmem_nonfree_pages --;
       list_add_head(free_ppage, ppage_descr);
 
       /* Indicate that the page is now unreferenced */
       retval = TRUE;
     }
 
+  /* The page was already referenced by someone */
   return retval;
 }
 
 
+sos_ret_t sos_physmem_get_physpage_refcount(sos_paddr_t ppage_paddr)
+{
+  struct physical_page_descr *ppage_descr
+    = get_page_descr_at_paddr(ppage_paddr);
+
+  if (! ppage_descr)
+    return -SOS_EINVAL;
+
+  return ppage_descr->ref_cnt;
+}
+
+
+sos_ret_t sos_physmem_inc_physpage_occupation(sos_paddr_t ppage_paddr)
+{
+  struct physical_page_descr *ppage_descr
+    = get_page_descr_at_paddr(ppage_paddr);
+
+  if (! ppage_descr)
+    return -SOS_EINVAL;
+
+  /* Don't do anything if the page is not in the nonfree list */
+  SOS_ASSERT_FATAL(ppage_descr->ref_cnt > 0);
+
+  ppage_descr->occupation_cnt ++;
+  return (ppage_descr->occupation_cnt > 1);
+}
+
+
+sos_ret_t sos_physmem_dec_physpage_occupation(sos_paddr_t ppage_paddr)
+{
+  struct physical_page_descr *ppage_descr
+    = get_page_descr_at_paddr(ppage_paddr);
+
+  if (! ppage_descr)
+    return -SOS_EINVAL;
+
+  /* Don't do anything if the page is not in the nonfree list */
+  SOS_ASSERT_FATAL(ppage_descr->ref_cnt > 0);
+  SOS_ASSERT_FATAL(ppage_descr->occupation_cnt > 0);
+
+  ppage_descr->occupation_cnt --;
+  return (ppage_descr->occupation_cnt == 0);
+}
+
+
 struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr)
 {
   struct physical_page_descr *ppage_descr
@@ -308,11 +375,11 @@
 }
 
 sos_ret_t sos_physmem_get_state(/* out */sos_count_t *total_ppages,
-				/* out */sos_count_t *used_ppages)
+				/* out */sos_count_t *nonfree_ppages)
 {
   if (total_ppages)
     *total_ppages = physmem_total_pages;
-  if (used_ppages)
-    *used_ppages = physmem_used_pages;
+  if (nonfree_ppages)
+    *nonfree_ppages = physmem_nonfree_pages;
   return SOS_OK;
 }
diff -ruN /tmp/sos-code-article6.75/sos/physmem.h ../sos-code-article7/sos/physmem.h
--- /tmp/sos-code-article6.75/sos/physmem.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/physmem.h	2005-04-27 20:14:21.000000000 +0200
@@ -1,4 +1,5 @@
 /* Copyright (C) 2004  David Decotigny
+   Copyright (C) 2000  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
@@ -46,7 +47,7 @@
 
 /**
  * This is the reserved physical interval for the x86 video memory and
- * BIOS area. In physmem.c, we have to mark this area as "used" in
+ * BIOS area. In physmem.c, we have to mark this area as "nonfree" in
  * order to prevent from allocating it. And in paging.c, we'd better
  * map it in virtual space if we really want to be able to print to
  * the screen (for debugging purpose, at least): for this, the
@@ -80,7 +81,7 @@
  * Retrieve the total number of pages, and the number of free pages
  */
 sos_ret_t sos_physmem_get_state(/* out */sos_count_t *total_ppages,
-				/* out */sos_count_t *used_ppages);
+				/* out */sos_count_t *nonfree_ppages);
 
 
 /**
@@ -101,26 +102,81 @@
  *
  * @param ppage_paddr Physical address of the page (MUST be page-aligned)
  *
- * @return TRUE when the page was previously in use, FALSE when the
- * page was previously in the free list, <0 when the page address is
+ * @return TRUE when the page was previously referenced, FALSE when
+ * the page was previously unreferenced, <0 when the page address is
  * invalid.
  */
 sos_ret_t sos_physmem_ref_physpage_at(sos_paddr_t ppage_paddr);
 
 
 /**
- * Decrement the reference count of the given physical page. When this
- * reference count reaches 0, the page is marked free, ie is available
- * for future sos_physmem_get_physpage()
+ * Decrement the reference count of the given physical page. When the
+ * reference count of the page reaches 0, the page is marked free, ie
+ * is available for future sos_physmem_ref_physpage_new()
  *
  * @param ppage_paddr Physical address of the page (MUST be page-aligned)
  *
- * @return FALSE when the page is still in use, TRUE when the page is now
- * unreferenced, <0 when the page address is invalid
+ * @return FALSE when the page is still referenced, TRUE when the page
+ * is now unreferenced, <0 when the page address is invalid
  */
 sos_ret_t sos_physmem_unref_physpage(sos_paddr_t ppage_paddr);
 
 
+/**
+ * Return the reference count of the given page
+ *
+ * @return >= 0 (the referebce count of the page) if the physical
+ * address is valid, or an error status
+ */
+sos_ret_t sos_physmem_get_physpage_refcount(sos_paddr_t ppage_paddr);
+
+
+
+/*
+ * In some cases (Page Tables on x86 for example), the physical pages
+ * might contain a set of slots available for "allocation" (x86
+ * example: a PT contains a set of PTE). We provide here a set of
+ * functions to manage a per-page additional counter that holds this
+ * number of slots currently in use in the page.
+ *
+ * A newly allocated physical page has a occupation count of 0.
+ *
+ * The management of this counter is up to the subsystem that manages
+ * the page (mainly: paging), however a simple set of rules applies:
+ *   - it can only be incremented/decremented if the page is referenced
+ *   - when it reaches 0, no automatic action is undertaken
+ * The first rule means in particular that a page whose reference
+ * count reaches 0 (=> being freed) cannot have a occupation_cnt
+ * value > 0 ! The system will be HALTED if this ever happens
+ */
+
+
+/**
+ * Increment the occupation count of the given physical page.
+ *
+ * @param ppage_paddr Physical address of the page (MUST be page-aligned)
+ *
+ * @return TRUE when the occupation count was previously NON-ZERO, FALSE
+ * when this occupation count was 0, <0 when the page address is invalid.
+ *
+ * @note the page MUST be marked as REFERENCED by somebody !
+ */
+sos_ret_t sos_physmem_inc_physpage_occupation(sos_paddr_t ppage_paddr);
+
+
+/**
+ * Decrement the occupation count of the given physical page.
+ *
+ * @param ppage_paddr Physical address of the page (MUST be page-aligned)
+ *
+ * @return FALSE when the occupation count is still NON-ZERO, TRUE when the
+ * page now is ZERO, <0 when the page address is invalid
+ *
+ * @note the page MUST be marked as REFERENCED by somebody !
+ */
+sos_ret_t sos_physmem_dec_physpage_occupation(sos_paddr_t ppage_paddr);
+
+
 #include <sos/kmem_vmm.h>
 
 /**
diff -ruN /tmp/sos-code-article6.75/sos/process.c ../sos-code-article7/sos/process.c
--- /tmp/sos-code-article6.75/sos/process.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/process.c	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,280 @@
+/* Copyright (C) 2005 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/assert.h>
+#include <sos/list.h>
+#include <sos/kmem_slab.h>
+#include <hwcore/irq.h>
+#include <drivers/bochs.h>
+
+#include <hwcore/mm_context.h>
+
+#include "process.h"
+
+
+#define SOS_PROCESS_MAX_NAMELEN 32
+
+
+/**
+ * Definition of a process in SOS. A process is basically the
+ * collection of all the resources requested by a user program. The
+ * threads are one of these resources, the mm_context is one other
+ * example of such resources.
+ */
+struct sos_process
+{
+  char name[SOS_PROCESS_MAX_NAMELEN];
+
+  /** First important resource: the MMU translation tables'
+      configuration */
+  struct sos_mm_context  *mm_context;
+
+  /** Second important resource: the CPU execution contexts, aka the
+      threads executing in the context of this process. */
+  struct sos_thread      *thread_list;
+  sos_count_t            nb_threads;
+
+  /** Reference counter, including threads */
+  sos_count_t            ref_cnt;
+
+  struct sos_process     *prev, *next;
+};
+
+
+/** The list of processes in the system */
+static struct sos_process *process_list = NULL;
+
+
+/** The cache for the sos_process structures */
+struct sos_kslab_cache *cache_struct_process;
+
+
+/**
+ * Helper function for debugging purposes
+ */
+void sos_process_dumplist()
+{
+  struct sos_thread * cur_thr = sos_thread_get_current();
+  struct sos_process * proc;
+  int nb_procs;
+
+  sos_bochs_printf("<ps>\n");
+  list_foreach(process_list, proc, nb_procs)
+    {
+      struct sos_thread * thr;
+      int    nb_thrs;
+
+      sos_bochs_printf("  proc@%p: '%s', %d threads, %d refs\n",
+		       proc, proc->name?proc->name:"(none)",
+		       proc->nb_threads, proc->ref_cnt);
+
+      list_foreach_forward_named(proc->thread_list, thr, nb_thrs,
+				 prev_in_process, next_in_process)
+	{
+	  if (thr == cur_thr)
+	    sos_bochs_printf("    thr@%p: [CURRENT]\n", thr);
+	  else
+	    sos_bochs_printf("    thr@%p: %cstate=%d eip=%p\n",
+			     thr,
+			     sos_cpu_context_is_in_user_mode(thr->cpu_state)?'u':'k',
+			     thr->state,
+			     (void*)sos_cpu_context_get_PC(thr->cpu_state));
+	}
+    }
+  sos_bochs_printf("  ======= %d processes =======\n", nb_procs);
+  sos_bochs_printf("</ps>\n");
+}
+
+
+sos_ret_t sos_process_subsystem_setup()
+{
+  /* Create the cache for the process structures */
+  cache_struct_process = sos_kmem_cache_create("struct_process",
+					       sizeof(struct sos_process),
+					       3,
+					       0,
+					       SOS_KSLAB_CREATE_MAP
+					       | SOS_KSLAB_CREATE_ZERO);
+  if (! cache_struct_process)
+    return -SOS_ENOMEM;
+
+  return SOS_OK;
+}
+
+
+struct sos_process *sos_process_create_empty(const char *name)
+{
+  sos_ui32_t flags;
+  struct sos_process *proc;
+
+  proc = (struct sos_process*) sos_kmem_cache_alloc(cache_struct_process, 0);
+  if (! proc)
+    return NULL;
+
+  /* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO
+     flag) */
+
+  /* Initialize a new mm_context */
+  proc->mm_context = sos_mm_context_create();
+  if (NULL == proc->mm_context)
+    {
+      /* Error */
+      sos_kmem_cache_free((sos_vaddr_t)proc);
+      return NULL;
+    }
+
+  if (!name)
+    {
+	name = "[UNNAMED]";
+    }
+
+  strzcpy(proc->name, name, SOS_PROCESS_MAX_NAMELEN);
+
+  /* Add it to the global list of processes */
+  sos_disable_IRQs(flags);
+  list_add_tail(process_list, proc);
+  sos_restore_IRQs(flags);
+
+  /* Mark the process as referenced */
+  proc->ref_cnt = 1;
+  return proc;
+}
+
+
+inline
+sos_ret_t sos_process_ref(struct sos_process *proc)
+{
+  sos_ui32_t flags;
+  sos_disable_IRQs(flags);
+  proc->ref_cnt ++;
+  sos_restore_IRQs(flags);
+  return SOS_OK;
+}
+
+
+sos_count_t sos_process_get_nb_threads(const struct sos_process *proc)
+{
+  sos_count_t retval;
+  sos_ui32_t flags;
+  sos_disable_IRQs(flags);
+  retval = proc->nb_threads;
+  sos_restore_IRQs(flags);
+  return retval;
+}
+
+
+struct sos_mm_context *
+sos_process_get_mm_context(const struct sos_process *proc)
+{
+  return proc->mm_context;
+}
+
+
+/* ***************************************************
+ * Restricted functions
+ */
+
+
+sos_ret_t sos_process_register_thread(struct sos_process *in_proc,
+				      struct sos_thread *thr)
+{
+  sos_ui32_t flags;
+
+  /* The process is assumed to be referenced by somebody */
+  SOS_ASSERT_FATAL(in_proc->ref_cnt > 0);
+
+  /* Only threads that are being created are allowed to be attached to
+     a process */
+  SOS_ASSERT_FATAL(thr->state == SOS_THR_CREATED);
+
+  /* Update the list of the threads in the process */
+  thr->process = in_proc;
+
+  /* Increment the reference count for the process */
+  sos_process_ref(in_proc);
+
+  /* Add the thread to the process thread's list */
+  sos_disable_IRQs(flags);
+  list_add_tail_named(in_proc->thread_list, thr,
+		      prev_in_process, next_in_process);
+  in_proc->nb_threads ++;
+  sos_restore_IRQs(flags);
+
+  return SOS_OK;
+}
+
+
+/** The function responsible for releasing the resources held by the
+    process. */
+sos_ret_t sos_process_unref(struct sos_process *proc)
+{
+  sos_ui32_t flags;
+  sos_ret_t retval;
+
+  SOS_ASSERT_FATAL(proc->ref_cnt > 0);
+
+  sos_disable_IRQs(flags);
+  proc->ref_cnt --;
+  if (proc->ref_cnt > 0)
+    {
+      sos_restore_IRQs(flags);
+      return -SOS_EBUSY;
+    }
+  list_delete(process_list, proc);
+  sos_restore_IRQs(flags);
+
+  /* First: release the mm_context */
+  retval = sos_mm_context_unref(proc->mm_context);
+  SOS_ASSERT_FATAL(SOS_OK == retval);
+
+  /* Free the process structure */
+  sos_kmem_cache_free((sos_vaddr_t)proc);
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_process_unregister_thread(struct sos_thread *thr)
+{
+  sos_ui32_t flags;
+  struct sos_process * in_proc = thr->process;
+
+  SOS_ASSERT_FATAL(thr->state == SOS_THR_ZOMBIE);
+  
+  /* Update the list of the threads in the process */
+  thr->process = NULL;
+  sos_disable_IRQs(flags);
+  list_delete_named(in_proc->thread_list, thr,
+		    prev_in_process, next_in_process);
+  SOS_ASSERT_FATAL(in_proc->nb_threads > 0);
+  in_proc->nb_threads --;
+  sos_restore_IRQs(flags);
+ 
+  /* Unreference the process */
+  sos_process_unref(in_proc);
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_process_set_name(struct sos_process * proc,
+			       const char * new_name)
+{
+  strzcpy(proc->name, new_name, SOS_PROCESS_MAX_NAMELEN);
+  return SOS_OK;
+}
diff -ruN /tmp/sos-code-article6.75/sos/process.h ../sos-code-article7/sos/process.h
--- /tmp/sos-code-article6.75/sos/process.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/process.h	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,138 @@
+/* Copyright (C) 2005 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_PROCESS_H_
+#define _SOS_PROCESS_H_
+
+/**
+ * @file process.h
+ *
+ * SOS Definition of a process and associated management API. A
+ * process is basically the collection of all the resources requested
+ * by a user program. The threads are one of these resources, the
+ * mm_context is one other example of such resources.  In SOS, a
+ * "process" is mainly a resource manager, a container. It does not
+ * provide much advanced functionality apart from a reference counter.
+ *
+ * Only the user threads belong to a process. The kernel
+ * threads don't because the resources they hold are held by the whole
+ * kernel, thus by ALL the threads (kernel AND user): the notion of
+ * "process" doesn't have any meaning for them because they don't own
+ * any proper resource (apart from their stack).
+ */
+
+#include <sos/errno.h>
+
+
+/**
+ * The definition of an SOS process is opaque. @see process.c
+ */
+struct sos_process;
+
+#include <sos/thread.h>
+
+
+/**
+ * Default size of a user stack (8 MB)
+ */
+#define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
+
+
+/**
+ * Initialization of the process subsystem
+ */
+sos_ret_t sos_process_subsystem_setup();
+
+
+/**
+ * Initialize a new (empty) process and return a reference to it. This
+ * means that:
+ *  - A new mm_context has been initialized
+ *  - No threads belong to this process yet
+ *  - Nothing is mapped in user space
+ *  - No other resource is used
+ *
+ * @return NULL on error (not enough memory)
+ */
+struct sos_process *sos_process_create_empty(const char *name);
+
+
+/**
+ * Signal we're using another reference to a process.
+ */
+sos_ret_t sos_process_ref(struct sos_process *proc);
+
+
+/**
+ * Release the reference to a process. If nobody holds a reference to
+ * it and if the process does not have any thread anymore, the process
+ * is destroyed.
+ *
+ * @return -SOS_EBUSY when the process is still referenced afterwards
+ */
+sos_ret_t sos_process_unref(struct sos_process *proc);
+
+
+/**
+ * Return the number of threads currently registered in the process
+ */
+sos_count_t sos_process_get_nb_threads(const struct sos_process *proc);
+
+
+/**
+ * Retrieve the address of the MMU configuration description
+ *
+ * @return NULL on error
+ */
+struct sos_mm_context *
+sos_process_get_mm_context(const struct sos_process *proc);
+
+
+/* ***************************************************
+ * Restricted functions
+ */
+
+
+/**
+ * Change the name of the process
+ */
+sos_ret_t sos_process_set_name(struct sos_process * proc,
+			       const char * new_name);
+
+
+/**
+ * Attach the given thread to the process
+ *
+ * @note This function is called internally by thread.c . The thread
+ * is assumed to be in the "CREATED" state, ie one cannot attach a
+ * thread to some other process once it has been created.
+ */
+sos_ret_t sos_process_register_thread(struct sos_process *in_proc,
+				      struct sos_thread *thr);
+
+
+/**
+ * Remove the given thread from the given process threads' list. When
+ * the process becomes empty (ie does not contain any thread anymore
+ * and is not referenced by anybody), all its resources are released.
+ *
+ * @note This function is called internally by thread.c . The thread
+ * is assumed to be in the "ZOMBIE" state.
+ */
+sos_ret_t sos_process_unregister_thread(struct sos_thread *thr);
+
+#endif /* _SOS_PROCESS_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/sched.c ../sos-code-article7/sos/sched.c
--- /tmp/sos-code-article6.75/sos/sched.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/sched.c	2005-04-27 20:14:21.000000000 +0200
@@ -16,9 +16,11 @@
    USA. 
 */
 
+#include <sos/errno.h>
 #include <sos/klibc.h>
 #include <sos/assert.h>
 #include <sos/list.h>
+#include <sos/calcload.h>
 
 #include "sched.h"
 
@@ -35,7 +37,7 @@
 struct sos_sched_queue
 {
   unsigned int nr_threads;
-  struct sos_kthread *kthread_list[SOS_SCHED_NUM_PRIO];
+  struct sos_thread *thread_list[SOS_SCHED_NUM_PRIO];
 };
 
 
@@ -53,12 +55,34 @@
 static struct sos_sched_queue sched_queue[2];
 
 
+/**
+ * The array giving the timeslice corresponding to each priority level
+ */
+struct sos_time time_slice[SOS_SCHED_NUM_PRIO];
+
+
 sos_ret_t sos_sched_subsystem_setup()
 {
+  sos_sched_priority_t prio;
+
   memset(sched_queue, 0x0, sizeof(sched_queue));
   active_queue  = & sched_queue[0];
   expired_queue = & sched_queue[1];
 
+  /* pre-compute time slices */
+  for (prio = SOS_SCHED_PRIO_TS_HIGHEST ;
+       prio <= SOS_SCHED_PRIO_TS_LOWEST ;
+       prio ++)
+    {
+      unsigned int ms;
+      ms = SOS_TIME_SLICE_MIN
+	   + (SOS_TIME_SLICE_MAX - SOS_TIME_SLICE_MIN)
+	     * (prio - SOS_SCHED_PRIO_TS_HIGHEST)
+	     / (SOS_SCHED_PRIO_TS_LOWEST - SOS_SCHED_PRIO_TS_HIGHEST);
+      time_slice[prio].sec     = ms / 1000;
+      time_slice[prio].nanosec = 1000000UL * (ms % 1000);
+    }
+
   return SOS_OK;
 }
 
@@ -71,42 +95,45 @@
  * the ready list. Otherwise it is added at the head of it.
  */
 static sos_ret_t add_in_ready_queue(struct sos_sched_queue *q,
-				    struct sos_kthread *thr,
+				    struct sos_thread *thr,
 				    sos_bool_t insert_at_tail)
 {
   sos_sched_priority_t prio;
 
-  SOS_ASSERT_FATAL( (SOS_KTHR_CREATED == thr->state)
-		    || (SOS_KTHR_RUNNING == thr->state) /* Yield */
-		    || (SOS_KTHR_BLOCKED == thr->state) );
+  SOS_ASSERT_FATAL( (SOS_THR_CREATED == thr->state)
+		    || (SOS_THR_RUNNING == thr->state) /* Yield */
+		    || (SOS_THR_BLOCKED == thr->state) );
 
   /* Add the thread to the CPU queue */
-  prio = sos_kthread_get_priority(thr);
+  prio = sos_thread_get_priority(thr);
   if (insert_at_tail)
-    list_add_tail_named(q->kthread_list[prio], thr,
+    list_add_tail_named(q->thread_list[prio], thr,
 			ready.rdy_prev, ready.rdy_next);
   else
-    list_add_head_named(q->kthread_list[prio], thr,
+    list_add_head_named(q->thread_list[prio], thr,
 			ready.rdy_prev, ready.rdy_next);
   thr->ready.rdy_queue = q;
   q->nr_threads ++;
 
   /* Ok, thread is now really ready to be (re)started */
-  thr->state = SOS_KTHR_READY;
+  thr->state = SOS_THR_READY;
 
   return SOS_OK;
 }
 
 
-sos_ret_t sos_sched_set_ready(struct sos_kthread *thr)
+sos_ret_t sos_sched_set_ready(struct sos_thread *thr)
 {
   sos_ret_t retval;
 
   /* Don't do anything for already ready threads */
-  if (SOS_KTHR_READY == thr->state)
+  if (SOS_THR_READY == thr->state)
     return SOS_OK;
 
-  if (SOS_SCHED_PRIO_IS_RT(sos_kthread_get_priority(thr)))
+  /* Reset the CPU time used in the quantuum */
+  memset(& thr->running.user_time_spent_in_slice, 0x0, sizeof(struct sos_time));
+
+  if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(thr)))
     {
       /* Real-time thread: schedule it for the present turn */
       retval = add_in_ready_queue(active_queue, thr, TRUE);
@@ -121,54 +148,89 @@
 }
 
 
-sos_ret_t sos_sched_change_priority(struct sos_kthread *thr,
+sos_ret_t sos_sched_change_priority(struct sos_thread *thr,
 				    sos_sched_priority_t priority)
 {
-  struct sos_kthread *kthread_list;
-  SOS_ASSERT_FATAL(SOS_KTHR_READY == thr->state);
+  struct sos_thread *thread_list;
+  SOS_ASSERT_FATAL(SOS_THR_READY == thr->state);
 
   /* Temp variable */
-  kthread_list
-    = thr->ready.rdy_queue->kthread_list[sos_kthread_get_priority(thr)];
+  thread_list
+    = thr->ready.rdy_queue->thread_list[sos_thread_get_priority(thr)];
 
-  list_delete_named(kthread_list, thr, ready.rdy_prev, ready.rdy_next);
+  list_delete_named(thread_list, thr, ready.rdy_prev, ready.rdy_next);
 
   /* Update lists */
-  kthread_list = thr->ready.rdy_queue->kthread_list[priority];
-  list_add_tail_named(kthread_list, thr, ready.rdy_prev, ready.rdy_next);
-  thr->ready.rdy_queue->kthread_list[priority] = kthread_list;
+  thread_list = thr->ready.rdy_queue->thread_list[priority];
+  list_add_tail_named(thread_list, thr, ready.rdy_prev, ready.rdy_next);
+  thr->ready.rdy_queue->thread_list[priority] = thread_list;
 
   return SOS_OK;
 }
 
 
-struct sos_kthread * sos_reschedule(struct sos_kthread *current_kthread,
-				    sos_bool_t do_yield)
+/**
+ * Helper function to determine whether the current thread expired its
+ * time quantuum
+ */
+static sos_bool_t
+thread_expired_its_quantuum(struct sos_thread *thr)
+{
+  sos_sched_priority_t prio = sos_thread_get_priority(thr);
+
+  /* No timesharing/round-robin for "real-time" threads */
+  if (SOS_SCHED_PRIO_IS_RT(prio))
+    return FALSE;
+
+  /* Current (user) thread expired its time quantuum ?  A kernel
+     thread never expires because sos_sched_do_timer_tick() below
+     won't update its user_time_spent_in_slice */
+  if (sos_time_cmp(& thr->running.user_time_spent_in_slice,
+		   & time_slice[prio]) >= 0)
+      return TRUE;
+
+  return FALSE;
+}
+
+
+struct sos_thread * sos_reschedule(struct sos_thread *current_thread,
+				   sos_bool_t do_yield)
 {
   sos_sched_priority_t prio;
 
-  if (SOS_KTHR_ZOMBIE == current_kthread->state)
+  /* Force the current thread to release the CPU if it expired its
+     quantuum */
+  if (thread_expired_its_quantuum(current_thread))
+    {
+      /* Reset the CPU time used in the quantuum */
+      memset(& current_thread->running.user_time_spent_in_slice,
+	     0x0, sizeof(struct sos_time));
+
+      do_yield = TRUE;
+    }
+
+  if (SOS_THR_ZOMBIE == current_thread->state)
     {
       /* Don't think of returning to this thread since it is
 	 terminated */
       /* Nop */
     }
-  else if (SOS_KTHR_BLOCKED != current_kthread->state)
+  else if (SOS_THR_BLOCKED != current_thread->state)
     {
       /* Take into account the current executing thread unless it is
 	 marked blocked */
       if (do_yield)
 	{
 	  /* Ok, reserve it for next turn */
-	  if (SOS_SCHED_PRIO_IS_RT(sos_kthread_get_priority(current_kthread)))
-	    add_in_ready_queue(active_queue, current_kthread, TRUE);
+	  if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(current_thread)))
+	    add_in_ready_queue(active_queue, current_thread, TRUE);
 	  else
-	    add_in_ready_queue(expired_queue, current_kthread, TRUE);
+	    add_in_ready_queue(expired_queue, current_thread, TRUE);
 	}
       else
 	{
 	  /* Put it at the head of the active list */
-	  add_in_ready_queue(active_queue, current_kthread, FALSE);
+	  add_in_ready_queue(active_queue, current_thread, FALSE);
 	}
     }
 
@@ -187,14 +249,14 @@
      non-empty queue */
   for (prio = SOS_SCHED_PRIO_HIGHEST ; prio <= SOS_SCHED_PRIO_LOWEST ; prio ++)
     {
-      struct sos_kthread *next_thr;
+      struct sos_thread *next_thr;
 
-      if (list_is_empty_named(active_queue->kthread_list[prio],
+      if (list_is_empty_named(active_queue->thread_list[prio],
 			      ready.rdy_prev, ready.rdy_next))
 	continue;
       
       /* Queue is not empty: take the thread at its head */
-      next_thr = list_pop_head_named(active_queue->kthread_list[prio],
+      next_thr = list_pop_head_named(active_queue->thread_list[prio],
 				     ready.rdy_prev, ready.rdy_next);
       active_queue->nr_threads --;
 
@@ -205,3 +267,72 @@
   SOS_FATAL_ERROR("No kernel thread ready ?!");
   return NULL;
 }
+
+
+sos_ret_t sos_sched_do_timer_tick()
+{
+  struct sos_thread *interrupted_thread = sos_thread_get_current();
+  struct sos_time tick_duration;
+  sos_bool_t cur_is_user;
+  sos_ui32_t nb_user_ready = 0;
+  sos_ui32_t nb_kernel_ready = 0;
+  int prio;
+
+  sos_time_get_tick_resolution(& tick_duration);
+
+  /* Update the timing statistics */
+  if (sos_cpu_context_is_in_user_mode(interrupted_thread->cpu_state))
+    {
+      cur_is_user = TRUE;
+
+      /* User time */
+      sos_time_inc(& interrupted_thread->rusage.ru_utime,
+		   & tick_duration);
+
+      /* Update time spent is current timeslice ONLY for a user thread */
+      sos_time_inc(& interrupted_thread->running.user_time_spent_in_slice,
+		   & tick_duration);
+    }
+  else
+    {
+      cur_is_user = FALSE;
+
+      /* System time */
+      sos_time_inc(& interrupted_thread->rusage.ru_stime,
+		   & tick_duration);
+    }
+
+
+  /* Update load stats */
+  for (prio = SOS_SCHED_PRIO_HIGHEST ; prio <= SOS_SCHED_PRIO_LOWEST ; prio ++)
+    {
+      struct sos_thread *thr;
+      int nb_thrs;
+
+      list_foreach_forward_named(active_queue->thread_list[prio],
+				 thr, nb_thrs,
+				 ready.rdy_prev, ready.rdy_next)
+	{
+	  if (sos_cpu_context_is_in_user_mode(thr->cpu_state))
+	    nb_user_ready ++;
+	  else
+	    nb_kernel_ready ++;
+	}
+
+      list_foreach_forward_named(expired_queue->thread_list[prio],
+				 thr, nb_thrs,
+				 ready.rdy_prev, ready.rdy_next)
+	{
+	  if (sos_cpu_context_is_in_user_mode(thr->cpu_state))
+	    nb_user_ready ++;
+	  else
+	    nb_kernel_ready ++;
+	}
+    }
+
+  sos_load_do_timer_tick(cur_is_user,
+			 nb_user_ready,
+			 nb_kernel_ready);
+
+  return SOS_OK;
+}
diff -ruN /tmp/sos-code-article6.75/sos/sched.h ../sos-code-article7/sos/sched.h
--- /tmp/sos-code-article6.75/sos/sched.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/sched.h	2005-04-27 20:14:21.000000000 +0200
@@ -36,18 +36,19 @@
  *    user does not change it.
  *
  * The functions below manage CPU queues, and are NEVER responsible
- * for context switches (see kthread.h for that) or synchronizations
+ * for context switches (see thread.h for that) or synchronizations
  * (see kwaitq.h or the higher levels primitives [mutex, semaphore,
  * ...] for that).
  *
  * @note IMPORTANT: all the functions below are meant to be called
- * ONLY by the kthread/timer/kwaitq subsystems. DO NOT use them
- * directly from anywhere else: use ONLY the kthread/kwaitq functions!
+ * ONLY by the thread/timer/kwaitq subsystems. DO NOT use them
+ * directly from anywhere else: use ONLY the thread/kwaitq functions!
  * If you still want to call them directly despite this disclaimer,
  * simply disable interrupts before clling them.
  */
 
 #include <sos/errno.h>
+#include <sos/time.h>
 
 
 /**
@@ -56,7 +57,7 @@
 typedef unsigned char sos_sched_priority_t;
 
 
-#include <sos/kthread.h>
+#include <sos/thread.h>
 
 
 /**
@@ -99,6 +100,13 @@
 
 
 /**
+ * The time slices for 'time-sharing' user threads, in ms
+ */
+#define SOS_TIME_SLICE_MIN  10 /* for SOS_SCHED_PRIO_TS_HIGHEST */
+#define SOS_TIME_SLICE_MAX 200 /* for SOS_SCHED_PRIO_TS_LOWEST */
+
+
+/**
  * Initialize the scheduler
  *
  * @note: The use of this function is RESERVED
@@ -111,31 +119,40 @@
  *
  * @note: The use of this function is RESERVED
  */
-sos_ret_t sos_sched_set_ready(struct sos_kthread * thr);
+sos_ret_t sos_sched_set_ready(struct sos_thread * thr);
 
 
 /**
- * Return the identifier of the next kthread to run. Also removes it
- * from the ready list, but does NOT set is as current_kthread !
+ * Return the identifier of the next thread to run. Also removes it
+ * from the ready list, but does NOT set is as current_thread !
  *
- * @param current_kthread TCB of the thread calling the function
+ * @param current_thread TCB of the thread calling the function
  *
  * @param do_yield When TRUE, put the current executing thread at the
  * end of the ready list. Otherwise it is kept at the head of it.
  *
  * @note: The use of this function is RESERVED
  */
-struct sos_kthread * sos_reschedule(struct sos_kthread * current_kthread,
+struct sos_thread * sos_reschedule(struct sos_thread * current_thread,
 				    sos_bool_t do_yield);
 
 /**
- * Called by kthread subsystem each time a READY thread's priority is
+ * Called by thread subsystem each time a READY thread's priority is
  * changed
  *
- * @note: The use of this function is RESERVED (to kthread.c)
+ * @note: The use of this function is RESERVED (to thread.c)
  */
-sos_ret_t sos_sched_change_priority(struct sos_kthread * thr,
+sos_ret_t sos_sched_change_priority(struct sos_thread * thr,
 				    sos_sched_priority_t priority);
 
 
+/**
+ * Account for the execution of a time tick. Should be called
+ * immediately before the timer ISR calls sos_reschedule() before
+ * returning to thread context.
+ *
+ * @note The use of this function is RESERVED (to timer IRQ)
+ */
+sos_ret_t sos_sched_do_timer_tick();
+
 #endif /* _SOS_WAITQUEUE_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/syscall.c ../sos-code-article7/sos/syscall.c
--- /tmp/sos-code-article6.75/sos/syscall.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/syscall.c	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,142 @@
+/* Copyright (C) 2005  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/thread.h>
+#include <sos/kmalloc.h>
+#include <sos/klibc.h>
+#include <drivers/bochs.h>
+
+#include <hwcore/cpu_context.h>
+#include <sos/uaccess.h>
+#include "syscall.h"
+
+
+/**
+ * THE syscall entry point
+ */
+sos_ret_t sos_do_syscall(int syscall_id,
+			 const struct sos_cpu_state *user_ctxt)
+{
+  sos_ret_t retval;
+
+  switch(syscall_id)
+    {
+    case SOS_SYSCALL_ID_EXIT:
+      {
+	unsigned int status;
+	retval = sos_syscall_get1arg(user_ctxt, & status);
+	if (SOS_OK != retval)
+	  break;
+	sos_thread_exit();
+	retval = -SOS_EFATAL; /* Not reached */
+      }
+      break;
+    case SOS_SYSCALL_ID_BOCHS_WRITE:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t len;
+	char * str;
+	retval = sos_syscall_get2args(user_ctxt, & user_str, & len);
+	if (SOS_OK != retval)
+	  break;
+
+	str = (char*)sos_kmalloc(len + 1, 0);
+	if (str)
+	  {
+	    retval = sos_strzcpy_from_user(str, user_str, len+1);
+	    str[len] = '\0';
+	    if (SOS_OK == retval)
+	      {
+		sos_bochs_printf("THR 0x%x: ",
+				 (unsigned)sos_thread_get_current());
+		retval = sos_bochs_putstring(str);
+		retval = len;
+	      }
+	    sos_kfree((sos_vaddr_t)str);
+	  }
+	else
+	  retval = -SOS_ENOMEM;
+      }
+      break;
+
+
+      /* ***********************************************
+       * Debug syscalls (will be removed in the future)
+       */
+
+
+      /**
+       * Syscall 4012: hexdump of a user-space memory region
+       * args: addr_start size, retval=ignored
+       */
+    case 4012:
+      {
+	sos_uaddr_t user_str;
+	unsigned int len;
+	unsigned char * str;
+
+	retval = sos_syscall_get2args(user_ctxt, & user_str, & len);
+	if (SOS_OK != retval)
+	  break;
+
+	str = (char*)sos_kmalloc(len + 1, 0);
+	if (str)
+	  {
+	    int i;
+	    sos_bochs_printf("Hexdump(0x%x, %d):\n", user_str, len);
+	    retval = sos_memcpy_from_user((sos_vaddr_t) str, user_str, len);
+	    sos_bochs_printf("  (Successfully copied %d out of %d)\n",
+			     retval, len);
+
+	    for (i = 0 ; i < retval ; i++)
+	      {
+		if ((i % 32) == 0)
+		  sos_bochs_printf("%x:", i);
+		sos_bochs_printf(" %x", str[i]);
+		if (((i+1) % 32) == 0)
+		  sos_bochs_printf("\n");
+	      }
+	    if (i % 32)
+	      sos_bochs_printf("\n");
+
+	    sos_kfree((sos_vaddr_t)str);
+	  }
+	else
+	  retval = -SOS_ENOMEM;
+      }
+      break;
+
+
+      /**
+       * Syscall 4008: dump the list of processes in the system
+       * args: none, retval=ignored
+       */
+    case 4008:
+      {
+	extern void sos_process_dumplist(void);
+	sos_process_dumplist();
+	retval = SOS_OK;
+      }
+      break;
+
+    default:
+      sos_bochs_printf("Syscall: UNKNOWN[%d]\n", syscall_id);
+      retval = -SOS_ENOSUP;
+    }
+  
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/sos/syscall.h ../sos-code-article7/sos/syscall.h
--- /tmp/sos-code-article6.75/sos/syscall.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/syscall.h	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,53 @@
+/* Copyright (C) 2005  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_SYSCALL_H_
+#define _SOS_SYSCALL_H_
+
+/**
+ * @file syscall.h
+ *
+ * SOS Syscalls identifiers and handler. The handler is called by the
+ * underlying assembler routine (see hwcore/syscall.h)
+ */
+
+
+/**
+ * The user services offered by the SOS kernel
+ */
+#define SOS_SYSCALL_ID_EXIT        256 /**< Args: retval, retval=NONE */
+
+#define SOS_SYSCALL_ID_BOCHS_WRITE 43  /**< Args: string, retval=num_printed */
+
+
+#if defined(KERNEL_SOS) && !defined(ASM_SOURCE)
+
+#include <sos/errno.h>
+#include <hwcore/cpu_context.h>
+
+/**
+ * "The" SOS syscall handler
+ *
+ * @param syscall_id The identifier of the syscall service
+ * @param user_ctxt The user context making the syscall
+ */
+sos_ret_t sos_do_syscall(int syscall_id,
+			 const struct sos_cpu_state *user_ctxt);
+
+#endif /* defined(KERNEL_SOS) && !defined(ASM_SOURCE) */
+
+#endif /* _SOS_SYSCALL_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/test-art7.c ../sos-code-article7/sos/test-art7.c
--- /tmp/sos-code-article6.75/sos/test-art7.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/test-art7.c	2005-04-27 20:14:22.000000000 +0200
@@ -0,0 +1,427 @@
+/* Copyright (C) 2005  David Decotigny
+   Copyright (C) 1995  TIS Committee (ELF typedefs, constants and macros)
+
+   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/types.h>
+#include <sos/klibc.h>
+#include <drivers/bochs.h>
+#include <sos/physmem.h>
+#include <sos/assert.h>
+
+#include <sos/process.h>
+#include <sos/thread.h>
+
+
+/**
+ * @file test-art7.c
+ *
+ * Basic tests for the user thread/process management API
+ */
+
+
+/**
+ * The "C" structure of a user program image in the kernel. Structures
+ * like this are created by the Makefile in the userland/ directory
+ */
+struct userprog_entry
+{
+  const char *name;
+  sos_vaddr_t bottom_vaddr;
+  sos_vaddr_t top_vaddr;
+};
+
+
+/**
+ * Symbol marking the start of the userprogs table, as setup by the
+ * ld script in the userland/ directory
+ */
+extern char _userprogs_table;
+
+
+/*
+ * Local functions
+ */
+
+
+/**
+ * Function to locate the given user program image in the kernel memory
+ */
+static struct userprog_entry * lookup_userprog(const char *name);
+
+
+/**
+ * Function to create a new process containing the given USER program
+ * image. This function automatically locates the destination addresses
+ * of the program by examinating its ELF header
+ *
+ * @return The address of the first instruction of the program, as
+ * given by its ELF header, or 0 when the program is not a correct ELF
+ * image.
+ */
+static sos_uaddr_t load_elf_prog(const struct userprog_entry *prog);
+
+
+/**
+ * Function that locates a USER program in the kernel image, creates a
+ * new USER process for it, and creates the given nb_uthreads USER
+ * threads inside it.
+ */
+static sos_ret_t spawn_program(const char *progname,
+			       unsigned nb_uthreads);
+
+
+/**
+ * The main function for our tests
+ */
+void test_art7()
+{
+  spawn_program("myprog5", 5);
+  spawn_program("myprog1", 10);
+  spawn_program("myprog5", 1);
+  spawn_program("myprog6", 12);
+  spawn_program("myprog2", 10);
+  spawn_program("myprog5", 1);
+  spawn_program("myprog3", 10);
+  spawn_program("myprog5", 1);
+  spawn_program("myprog1", 10);
+  spawn_program("myprog6", 12);
+  spawn_program("myprog5", 1);
+  spawn_program("myprog4", 10);
+  spawn_program("myprog5", 1);
+  spawn_program("myprog2", 10);
+  spawn_program("myprog6", 12);
+  spawn_program("myprog5", 1);
+}
+
+
+static sos_ret_t spawn_program(const char *progname,
+			       unsigned nb_uthreads)
+{
+  int i;
+  
+  sos_uaddr_t prog_entry, stack_top_uaddr;
+  struct userprog_entry *prog;
+  struct sos_process *new_proc;
+
+  prog = lookup_userprog(progname);
+  if (! prog)
+    return -SOS_EINVAL;
+
+  new_proc = sos_process_create_empty(progname);
+  if (! new_proc)
+    return -SOS_ENOMEM;
+
+  /* Squat this new process to map the user program into it */
+  SOS_ASSERT_FATAL(SOS_OK
+		   == sos_thread_change_current_mm_context(sos_process_get_mm_context(new_proc)));
+
+  /* Load the user program image */
+  prog_entry = load_elf_prog(prog);
+  if (! prog_entry)
+    {
+      sos_process_unref(new_proc);
+      return -SOS_ENOMEM;
+    }
+
+  /* Map the user stacks into it and create the user threads */
+  /* By default, the first user stack will be located at the end of
+     the user address space (ie 4GB), the stacks of the other threads
+     will be located (12 pages) below. Each stack is USTACK_NPAGES
+     pages long */  
+#define USTACK_NPAGES 8
+  for (i = 0, stack_top_uaddr = 0xfffffffc ;
+       i < nb_uthreads ;
+       i++, stack_top_uaddr -= (USTACK_NPAGES + 4)*SOS_PAGE_SIZE)
+    {
+      int p;
+      char thrname[16];
+      sos_uaddr_t stack_base = SOS_PAGE_ALIGN_INF(stack_top_uaddr);
+
+      /* Allocate USTACK_NPAGES pages for the stack and map them */
+      for (p = 0 ; p < USTACK_NPAGES ; p++, stack_base -= SOS_PAGE_SIZE)
+	{
+	  sos_ret_t retval;
+	  sos_paddr_t ppage;
+	  
+	  ppage = sos_physmem_ref_physpage_new(FALSE);
+	  SOS_ASSERT_FATAL(ppage != 0);
+	  
+	  /* Map it in the process space. Might fail is there is not
+	     enough RAM (we don't support swap-out for the moment) */
+	  retval = sos_paging_map(ppage, stack_base, TRUE,
+				  SOS_VM_MAP_PROT_READ
+				  | SOS_VM_MAP_PROT_WRITE);
+	  SOS_ASSERT_FATAL(retval == SOS_OK);
+	  
+	  retval = sos_physmem_unref_physpage(ppage);
+	  SOS_ASSERT_FATAL(retval == 0);
+	  
+	  /* Poison the stack to detect the use of uninitialized
+	     variables */
+	  memset((void*)stack_base, 0xa5, SOS_PAGE_SIZE);
+	}
+      
+      /* Create the user thread */
+      snprintf(thrname, sizeof(thrname), "%s:%d", progname, i);
+      sos_bochs_printf("Spawning %s\n", thrname);
+      sos_create_user_thread(thrname,
+			     new_proc,
+			     prog_entry,
+			     0, 0,
+			     stack_top_uaddr,
+			     SOS_SCHED_PRIO_TS_LOWEST);
+
+      sos_thread_yield();
+    }
+
+  /* Don't need the reference to the process anymore */
+  sos_process_unref(new_proc);
+
+  /* Revert to normal kernel thread's address space */
+  SOS_ASSERT_FATAL(SOS_OK
+		   == sos_thread_change_current_mm_context(NULL));
+
+  return SOS_OK;
+}
+
+
+/**
+ * Lookup a user program located inside the kernel's image
+ */
+static struct userprog_entry * lookup_userprog(const char *name)
+{
+  struct userprog_entry *prog;
+
+  if (! name)
+    return NULL;
+
+  /* Walk through the table of user program description structures to
+     find the user program with the given name */
+  for (prog = (struct userprog_entry*) & _userprogs_table ;
+       prog && (prog->name != NULL) ;
+       prog++)
+    {
+      if (0 == strcmp(name, prog->name))
+	/* Found it ! */
+	return prog;
+    }
+
+  return NULL;
+}
+
+
+/**
+ * Make sure the program is in a valid ELF format, map it into memory,
+ * and return the address of its entry point (ie _start function)
+ *
+ * @return 0 when the program is not a valid ELF
+ */
+static sos_uaddr_t load_elf_prog(const struct userprog_entry *prog)
+{
+  int i;
+
+  /**
+   * Typedefs, constants and structure definitions as given by the ELF
+   * standard specifications.
+   */
+  typedef unsigned long  Elf32_Addr;
+  typedef unsigned long  Elf32_Word;
+  typedef unsigned short Elf32_Half;
+  typedef unsigned long  Elf32_Off;
+  typedef signed long    Elf32_Sword;
+  
+  /* Elf identification */
+  
+#define EI_NIDENT 16
+  typedef struct {
+    unsigned char       e_ident[EI_NIDENT];
+    Elf32_Half          e_type;
+    Elf32_Half          e_machine;
+    Elf32_Word          e_version;
+    Elf32_Addr          e_entry;
+    Elf32_Off           e_phoff;
+    Elf32_Off           e_shoff;
+    Elf32_Word          e_flags;
+    Elf32_Half          e_ehsize;
+    Elf32_Half          e_phentsize;
+    Elf32_Half          e_phnum;
+    Elf32_Half          e_shentsize;
+    Elf32_Half          e_shnum;
+    Elf32_Half          e_shstrndx;
+  } __attribute__((packed)) Elf32_Ehdr_t;
+  
+/* e_ident value */
+#define ELFMAG0 0x7f
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+
+/* e_ident offsets */
+#define EI_MAG0         0
+#define EI_MAG1         1
+#define EI_MAG2         2
+#define EI_MAG3         3
+#define EI_CLASS        4
+#define EI_DATA         5
+#define EI_VERSION      6
+#define EI_PAD          7
+
+/* e_ident[EI_CLASS] */
+#define ELFCLASSNONE    0
+#define ELFCLASS32      1
+#define ELFCLASS64      2
+
+/* e_ident[EI_DATA] */
+#define ELFDATANONE     0
+#define ELFDATA2LSB     1
+#define ELFDATA2MSB     2
+
+/* e_type */
+#define ET_NONE         0  /* No file type       */
+#define ET_REL          1  /* Relocatable file   */
+#define ET_EXEC         2  /* Executable file    */
+#define ET_DYN          3  /* Shared object file */
+#define ET_CORE         4  /* Core file          */
+#define ET_LOPROC  0xff00  /* Processor-specific */
+#define ET_HIPROC  0xffff  /* Processor-specific */
+
+/* e_machine */
+#define EM_NONE       0  /* No machine     */
+#define EM_M32        1  /* AT&T WE 32100  */
+#define EM_SPARC      2  /* SPARC          */
+#define EM_386        3  /* Intel 80386    */
+#define EM_68K        4  /* Motorola 68000 */
+#define EM_88K        5  /* Motorola 88000 */
+#define EM_860        7  /* Intel 80860    */
+#define EM_MIPS       8  /* MIPS RS3000    */
+
+/* e_version */
+#define EV_NONE    0 /* invalid version */
+#define EV_CURRENT 1 /* current version */
+
+  typedef struct {
+    Elf32_Word    p_type;
+    Elf32_Off     p_offset;
+    Elf32_Addr    p_vaddr;
+    Elf32_Addr    p_paddr;
+    Elf32_Word    p_filesz;
+    Elf32_Word    p_memsz;
+    Elf32_Word    p_flags;
+    Elf32_Word    p_align;
+  } __attribute__((packed)) Elf32_Phdr_t;
+
+/* Reserved segment types p_type */
+#define PT_NULL    0
+#define PT_LOAD    1
+#define PT_DYNAMIC 2
+#define PT_INTERP  3
+#define PT_NOTE    4
+#define PT_SHLIB   5
+#define PT_PHDR    6
+#define PT_LOPROC  0x70000000
+#define PT_HIPROC  0x7fffffff
+
+/* p_flags */
+#define PF_X       1
+#define PF_W       2
+#define PF_R       4
+
+
+  Elf32_Ehdr_t *elf_hdr = (Elf32_Ehdr_t*) prog->bottom_vaddr;
+  Elf32_Phdr_t *elf_phdrs;
+
+  /* Make sure the image is large enough to contain at least the ELF
+     header */
+  if (prog->bottom_vaddr + sizeof(Elf32_Ehdr_t) > prog->top_vaddr)
+    {
+      sos_bochs_printf("ELF prog %s: incorrect header\n", prog->name);
+      return 0;
+    }
+
+  /* Macro to check expected values for some fields in the ELF header */
+#define ELF_CHECK(hdr,field,expected_value) \
+  ({ if ((hdr)->field != (expected_value)) \
+     { \
+       sos_bochs_printf("ELF prog %s: for %s, expected %x, got %x\n", \
+			prog->name, \
+			#field, \
+			(unsigned)(expected_value), \
+			(unsigned)(hdr)->field); \
+       return 0; \
+     } \
+  })
+
+  ELF_CHECK(elf_hdr, e_ident[EI_MAG0], ELFMAG0);
+  ELF_CHECK(elf_hdr, e_ident[EI_MAG1], ELFMAG1);
+  ELF_CHECK(elf_hdr, e_ident[EI_MAG2], ELFMAG2);
+  ELF_CHECK(elf_hdr, e_ident[EI_MAG3], ELFMAG3);
+  ELF_CHECK(elf_hdr, e_ident[EI_CLASS], ELFCLASS32);
+  ELF_CHECK(elf_hdr, e_ident[EI_DATA], ELFDATA2LSB);
+  ELF_CHECK(elf_hdr, e_type, ET_EXEC);
+  ELF_CHECK(elf_hdr, e_version, EV_CURRENT);
+
+  /* Get the begining of the program header table */
+  elf_phdrs = (Elf32_Phdr_t*) (prog->bottom_vaddr + elf_hdr->e_phoff);
+
+  /* Map the program segment in R/W mode. To make things clean, we
+     should iterate over the sections, not the program header */
+  for (i = 0 ; i < elf_hdr->e_phnum ; i++)
+    {
+      sos_uaddr_t uaddr;
+
+      /* Ignore the empty program headers that are not marked "LOAD" */
+      if (elf_phdrs[i].p_type != PT_LOAD)
+	{
+	  if (elf_phdrs[i].p_memsz != 0)
+	    {
+	      SOS_FATAL_ERROR("ELF: non-empty non-LOAD segments not supported yet");
+	    }
+	  continue;
+	}
+      
+      if (elf_phdrs[i].p_vaddr < SOS_PAGING_BASE_USER_ADDRESS)
+	{
+	  SOS_FATAL_ERROR("User program has an incorrect address");
+	}
+
+      /* Map pages of physical memory into user space */
+      for (uaddr = SOS_PAGE_ALIGN_INF(elf_phdrs[i].p_vaddr) ;
+	   uaddr < elf_phdrs[i].p_vaddr + elf_phdrs[i].p_memsz ;
+	   uaddr += SOS_PAGE_SIZE)
+	{
+	  sos_ret_t retval;
+	  sos_paddr_t ppage;
+	  ppage = sos_physmem_ref_physpage_new(TRUE);
+
+	  retval = sos_paging_map(ppage, uaddr, TRUE,
+				  SOS_VM_MAP_PROT_READ
+				  | SOS_VM_MAP_PROT_WRITE);
+	  SOS_ASSERT_FATAL(retval == SOS_OK);
+
+	  retval = sos_physmem_unref_physpage(ppage);
+	  SOS_ASSERT_FATAL(retval == 0);
+	}
+
+      /* Copy segment into memory */
+      memcpy((void*) elf_phdrs[i].p_vaddr,
+	     (void*) (prog->bottom_vaddr + elf_phdrs[i].p_offset),
+	     elf_phdrs[i].p_filesz);
+    }
+
+  return elf_hdr->e_entry;
+}
diff -ruN /tmp/sos-code-article6.75/sos/thread.c ../sos-code-article7/sos/thread.c
--- /tmp/sos-code-article6.75/sos/thread.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/thread.c	2005-04-27 20:14:21.000000000 +0200
@@ -0,0 +1,821 @@
+/* Copyright (C) 2004,2005 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/kmem_slab.h>
+#include <sos/kmalloc.h>
+#include <sos/klibc.h>
+#include <sos/list.h>
+#include <sos/assert.h>
+#include <hwcore/mm_context.h>
+#include <sos/process.h>
+
+#include <drivers/bochs.h>
+#include <drivers/x86_videomem.h>
+
+#include <hwcore/irq.h>
+
+#include "thread.h"
+
+
+/**
+ * The size of the stack of a kernel thread
+ */
+#define SOS_THREAD_KERNEL_STACK_SIZE (1*SOS_PAGE_SIZE)
+
+
+/**
+ * The identifier of the thread currently running on CPU.
+ *
+ * We only support a SINGLE processor, ie a SINGLE thread
+ * running at any time in the system. This greatly simplifies the
+ * implementation of the system, since we don't have to complicate
+ * things in order to retrieve the identifier of the threads running
+ * on the CPU. On multiprocessor systems the current_thread below is
+ * an array indexed by the id of the CPU, so that the challenge is to
+ * retrieve the identifier of the CPU. This is usually done based on
+ * the stack address (Linux implementation) or on some form of TLS
+ * ("Thread Local Storage": can be implemented by way of LDTs for the
+ * processes, accessed through the fs or gs registers).
+ */
+static volatile struct sos_thread *current_thread = NULL;
+
+
+/*
+ * The list of threads currently in the system.
+ *
+ * @note We could have used current_thread for that...
+ */
+static struct sos_thread *thread_list = NULL;
+
+
+/**
+ * The Cache of thread structures
+ */
+static struct sos_kslab_cache *cache_thread;
+
+
+struct sos_thread *sos_thread_get_current()
+{
+  SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);
+  return (struct sos_thread*)current_thread;
+}
+
+
+inline static sos_ret_t _set_current(struct sos_thread *thr)
+{
+  SOS_ASSERT_FATAL(thr->state == SOS_THR_READY);
+  current_thread = thr;
+  current_thread->state = SOS_THR_RUNNING;
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_thread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
+				     sos_size_t init_thread_stack_size)
+{
+  struct sos_thread *myself;
+
+  /* Allocate the cache of threads */
+  cache_thread = sos_kmem_cache_create("thread",
+				       sizeof(struct sos_thread),
+				       2,
+				       0,
+				       SOS_KSLAB_CREATE_MAP
+				       | SOS_KSLAB_CREATE_ZERO);
+  if (! cache_thread)
+    return -SOS_ENOMEM;
+
+  /* Allocate a new thread structure for the current running thread */
+  myself = (struct sos_thread*) sos_kmem_cache_alloc(cache_thread,
+						     SOS_KSLAB_ALLOC_ATOMIC);
+  if (! myself)
+    return -SOS_ENOMEM;
+
+  /* Initialize the thread attributes */
+  strzcpy(myself->name, "[kinit]", SOS_THR_MAX_NAMELEN);
+  myself->state           = SOS_THR_CREATED;
+  myself->priority        = SOS_SCHED_PRIO_LOWEST;
+  myself->kernel_stack_base_addr = init_thread_stack_base_addr;
+  myself->kernel_stack_size      = init_thread_stack_size;
+
+  /* Do some stack poisoning on the bottom of the stack, if needed */
+  sos_cpu_state_prepare_detect_kernel_stack_overflow(myself->cpu_state,
+						     myself->kernel_stack_base_addr,
+						     myself->kernel_stack_size);
+
+  /* Add the thread in the global list */
+  list_singleton_named(thread_list, myself, gbl_prev, gbl_next);
+
+  /* Ok, now pretend that the running thread is ourselves */
+  myself->state = SOS_THR_READY;
+  _set_current(myself);
+
+  return SOS_OK;
+}
+
+
+struct sos_thread *
+sos_create_kernel_thread(const char *name,
+			 sos_kernel_thread_start_routine_t start_func,
+			 void *start_arg,
+			 sos_sched_priority_t priority)
+{
+  __label__ undo_creation;
+  sos_ui32_t flags;
+  struct sos_thread *new_thread;
+
+  if (! start_func)
+    return NULL;
+  if (! SOS_SCHED_PRIO_IS_VALID(priority))
+    return NULL;
+
+  /* Allocate a new thread structure for the current running thread */
+  new_thread
+    = (struct sos_thread*) sos_kmem_cache_alloc(cache_thread,
+						SOS_KSLAB_ALLOC_ATOMIC);
+  if (! new_thread)
+    return NULL;
+
+  /* Initialize the thread attributes */
+  strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_THR_MAX_NAMELEN);
+  new_thread->state    = SOS_THR_CREATED;
+  new_thread->priority = priority;
+
+  /* Allocate the stack for the new thread */
+  new_thread->kernel_stack_base_addr = sos_kmalloc(SOS_THREAD_KERNEL_STACK_SIZE, 0);
+  new_thread->kernel_stack_size      = SOS_THREAD_KERNEL_STACK_SIZE;
+  if (! new_thread->kernel_stack_base_addr)
+    goto undo_creation;
+
+  /* Initialize the CPU context of the new thread */
+  if (SOS_OK
+      != sos_cpu_kstate_init(& new_thread->cpu_state,
+			     (sos_cpu_kstate_function_arg1_t*) start_func,
+			     (sos_ui32_t) start_arg,
+			     new_thread->kernel_stack_base_addr,
+			     new_thread->kernel_stack_size,
+			     (sos_cpu_kstate_function_arg1_t*) sos_thread_exit,
+			     (sos_ui32_t) NULL))
+    goto undo_creation;
+
+  /* Add the thread in the global list */
+  sos_disable_IRQs(flags);
+  list_add_tail_named(thread_list, new_thread, gbl_prev, gbl_next);
+  sos_restore_IRQs(flags);
+
+  /* Mark the thread ready */
+  if (SOS_OK != sos_sched_set_ready(new_thread))
+    goto undo_creation;
+
+  /* Normal non-erroneous end of function */
+  return new_thread;
+
+ undo_creation:
+  if (new_thread->kernel_stack_base_addr)
+    sos_kfree((sos_vaddr_t) new_thread->kernel_stack_base_addr);
+  sos_kmem_cache_free((sos_vaddr_t) new_thread);
+  return NULL;
+}
+
+
+struct sos_thread *
+sos_create_user_thread(const char *name,
+		       struct sos_process *process,
+		       sos_uaddr_t user_initial_PC,
+		       sos_ui32_t  user_start_arg1,
+		       sos_ui32_t  user_start_arg2,
+		       sos_uaddr_t user_initial_SP,
+		       sos_sched_priority_t priority)
+{
+  __label__ undo_creation;
+  sos_ui32_t flags;
+  struct sos_thread *new_thread;
+
+  if (! SOS_SCHED_PRIO_IS_VALID(priority))
+    return NULL;
+
+  /* For a user thread, the process must be given */
+  if (! process)
+    return NULL;
+
+  /* Allocate a new thread structure for the current running thread */
+  new_thread
+    = (struct sos_thread*) sos_kmem_cache_alloc(cache_thread,
+						SOS_KSLAB_ALLOC_ATOMIC);
+  if (! new_thread)
+    return NULL;
+
+  /* Initialize the thread attributes */
+  strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_THR_MAX_NAMELEN);
+  new_thread->state    = SOS_THR_CREATED;
+  new_thread->priority = priority;
+
+  /* Allocate the stack for the new thread */
+  new_thread->kernel_stack_base_addr = sos_kmalloc(SOS_THREAD_KERNEL_STACK_SIZE, 0);
+  new_thread->kernel_stack_size      = SOS_THREAD_KERNEL_STACK_SIZE;
+  if (! new_thread->kernel_stack_base_addr)
+    goto undo_creation;
+
+  if (SOS_OK
+      != sos_cpu_ustate_init(& new_thread->cpu_state,
+			     user_initial_PC,
+			     user_start_arg1,
+			     user_start_arg2,
+			     user_initial_SP,
+			     new_thread->kernel_stack_base_addr,
+			     new_thread->kernel_stack_size))
+    goto undo_creation;
+
+  /* Attach the new thread to the process */
+  if (SOS_OK != sos_process_register_thread(process, new_thread))
+    goto undo_creation;
+
+  /* Add the thread in the global list */
+  sos_disable_IRQs(flags);
+  list_add_tail_named(thread_list, new_thread, gbl_prev, gbl_next);
+  sos_restore_IRQs(flags);
+
+  /* Mark the thread ready */
+  if (SOS_OK != sos_sched_set_ready(new_thread))
+    goto undo_creation;
+
+  /* Normal non-erroneous end of function */
+  return new_thread;
+
+ undo_creation:
+  if (new_thread->kernel_stack_base_addr)
+    sos_kfree((sos_vaddr_t) new_thread->kernel_stack_base_addr);
+  sos_kmem_cache_free((sos_vaddr_t) new_thread);
+  return NULL;
+}
+
+
+/**
+ * Helper function to switch to the correct MMU configuration to suit
+ * the_thread's needs.
+ *   - When switching to a user-mode thread, force the reconfiguration
+ *     of the MMU
+ *   - When switching to a kernel-mode thread, only change the MMU
+ *     configuration if the thread was squatting someone else's space
+ */
+static void _prepare_mm_context(struct sos_thread *the_thread)
+{
+  /* Going to restore a thread in user mode ? */
+  if (sos_cpu_context_is_in_user_mode(the_thread->cpu_state)
+      == TRUE)
+    {
+      /* Yes: force the MMU to be correctly setup with the correct
+	 user's address space */
+
+      /* The thread should be a user thread */
+      SOS_ASSERT_FATAL(the_thread->process != NULL);
+
+      /* It should not squat any other's address space */
+      SOS_ASSERT_FATAL(the_thread->squatted_mm_context == NULL);
+
+      /* Perform an MMU context switch if needed */
+      sos_mm_context_switch_to(sos_process_get_mm_context(the_thread->process));
+    }
+
+  /* the_thread is a kernel thread squatting a precise address
+     space ? */
+  else if (the_thread->squatted_mm_context != NULL)
+    sos_mm_context_switch_to(the_thread->squatted_mm_context);
+}
+
+
+/** Function called after thr has terminated. Called from inside the context
+    of another thread, interrupts disabled */
+static void delete_thread(struct sos_thread *thr)
+{
+  sos_ui32_t flags;
+
+  sos_disable_IRQs(flags);
+  list_delete_named(thread_list, thr, gbl_prev, gbl_next);
+  sos_restore_IRQs(flags);
+
+  sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr);
+
+  /* If the thread squats an address space, release it */
+  if (thr->squatted_mm_context)
+    SOS_ASSERT_FATAL(SOS_OK == sos_thread_change_current_mm_context(NULL));
+
+  /* For a user thread: remove the thread from the process threads' list */
+  if (thr->process)
+    SOS_ASSERT_FATAL(SOS_OK == sos_process_unregister_thread(thr));
+
+  memset(thr, 0x0, sizeof(struct sos_thread));
+  sos_kmem_cache_free((sos_vaddr_t) thr);
+}
+
+
+void sos_thread_exit()
+{
+  sos_ui32_t flags;
+  struct sos_thread *myself, *next_thread;
+
+  /* Interrupt handlers are NOT allowed to exit the current thread ! */
+  SOS_ASSERT_FATAL(! sos_servicing_irq());
+
+  myself = sos_thread_get_current();
+
+  /* Refuse to end the current executing thread if it still holds a
+     resource ! */
+  SOS_ASSERT_FATAL(list_is_empty_named(myself->kwaitq_list,
+				       prev_entry_for_thread,
+				       next_entry_for_thread));
+
+  /* Prepare to run the next thread */
+  sos_disable_IRQs(flags);
+  myself->state = SOS_THR_ZOMBIE;
+  next_thread = sos_reschedule(myself, FALSE);
+
+  /* Make sure that the next_thread is valid */
+  sos_cpu_state_detect_kernel_stack_overflow(next_thread->cpu_state,
+					     next_thread->kernel_stack_base_addr,
+					     next_thread->kernel_stack_size);
+
+  /*
+   * Perform an MMU context switch if needed
+   */
+  _prepare_mm_context(next_thread);
+
+  /* No need for sos_restore_IRQs() here because the IRQ flag will be
+     restored to that of the next thread upon context switch */
+
+  /* Immediate switch to next thread */
+  _set_current(next_thread);
+  sos_cpu_context_exit_to(next_thread->cpu_state,
+			  (sos_cpu_kstate_function_arg1_t*) delete_thread,
+			  (sos_ui32_t) myself);
+}
+
+
+sos_sched_priority_t sos_thread_get_priority(struct sos_thread *thr)
+{
+  if (! thr)
+    thr = (struct sos_thread*)current_thread;
+
+  return thr->priority;
+}
+
+
+sos_thread_state_t sos_thread_get_state(struct sos_thread *thr)
+{
+  if (! thr)
+    thr = (struct sos_thread*)current_thread;
+
+  return thr->state;
+}
+
+
+typedef enum { YIELD_MYSELF, BLOCK_MYSELF } switch_type_t;
+/**
+ * Helper function to initiate a context switch in case the current
+ * thread becomes blocked, waiting for a timeout, or calls yield.
+ */
+static sos_ret_t _switch_to_next_thread(switch_type_t operation)
+{
+  struct sos_thread *myself, *next_thread;
+
+  SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);
+
+  /* Interrupt handlers are NOT allowed to block ! */
+  SOS_ASSERT_FATAL(! sos_servicing_irq());
+
+  myself = (struct sos_thread*)current_thread;
+
+  /* Make sure that if we are to be marked "BLOCKED", we have any
+     reason of effectively being blocked */
+  if (BLOCK_MYSELF == operation)
+    {
+      myself->state = SOS_THR_BLOCKED;
+    }
+
+  /* Identify the next thread */
+  next_thread = sos_reschedule(myself, YIELD_MYSELF == operation);
+
+  /* Avoid context switch if the context does not change */
+  if (myself != next_thread)
+    {
+      /* Sanity checks for the next thread */
+      sos_cpu_state_detect_kernel_stack_overflow(next_thread->cpu_state,
+						 next_thread->kernel_stack_base_addr,
+						 next_thread->kernel_stack_size);
+
+      /*
+       * Perform an MMU context switch if needed
+       */
+      _prepare_mm_context(next_thread);
+
+      /*
+       * Actual CPU context switch
+       */
+      _set_current(next_thread);
+      sos_cpu_context_switch(& myself->cpu_state, next_thread->cpu_state);
+      
+      /* Back here ! */
+      SOS_ASSERT_FATAL(current_thread == myself);
+      SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);
+    }
+  else
+    {
+      /* No context switch but still update ID of current thread */
+      _set_current(next_thread);
+    }
+
+  return SOS_OK;
+}
+
+
+/**
+ * Helper function to change the thread's priority in all the
+ * waitqueues associated with the thread.
+ */
+static sos_ret_t _change_waitq_priorities(struct sos_thread *thr,
+					  sos_sched_priority_t priority)
+{
+  struct sos_kwaitq_entry *kwq_entry;
+  int nb_waitqs;
+
+  list_foreach_forward_named(thr->kwaitq_list, kwq_entry, nb_waitqs,
+			     prev_entry_for_thread, next_entry_for_thread)
+    {
+      SOS_ASSERT_FATAL(SOS_OK == sos_kwaitq_change_priority(kwq_entry->kwaitq,
+							    kwq_entry,
+							    priority));
+    }
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_thread_set_priority(struct sos_thread *thr,
+				   sos_sched_priority_t priority)
+{
+  __label__ exit_set_prio;
+  sos_ui32_t flags;
+  sos_ret_t retval;
+
+
+  if (! SOS_SCHED_PRIO_IS_VALID(priority))
+    return -SOS_EINVAL;
+
+  if (! thr)
+    thr = (struct sos_thread*)current_thread;
+
+  sos_disable_IRQs(flags);
+
+  /* Signal kwaitq subsystem that the priority of the thread in all
+     the waitq it is waiting in should be updated */
+  retval = _change_waitq_priorities(thr, priority);
+  if (SOS_OK != retval)
+    goto exit_set_prio;
+
+  /* Signal scheduler that the thread, currently in a waiting list,
+     should take into account the change of priority */
+  if (SOS_THR_READY == thr->state)
+    retval = sos_sched_change_priority(thr, priority);
+
+  /* Update priority */
+  thr->priority = priority;
+
+ exit_set_prio:
+  sos_restore_IRQs(flags);
+  return retval;
+}
+
+
+sos_ret_t sos_thread_yield()
+{
+  sos_ui32_t flags;
+  sos_ret_t retval;
+
+  sos_disable_IRQs(flags);
+
+  retval = _switch_to_next_thread(YIELD_MYSELF);
+
+  sos_restore_IRQs(flags);
+  return retval;
+}
+
+
+/**
+ * Internal sleep timeout management
+ */
+struct sleep_timeout_params
+{
+  struct sos_thread *thread_to_wakeup;
+  sos_bool_t timeout_triggered;
+};
+
+
+/**
+ * Callback called when a timeout happened
+ */
+static void sleep_timeout(struct sos_timeout_action *act)
+{
+  struct sleep_timeout_params *sleep_timeout_params
+    = (struct sleep_timeout_params*) act->routine_data;
+
+  /* Signal that we have been woken up by the timeout */
+  sleep_timeout_params->timeout_triggered = TRUE;
+
+  /* Mark the thread ready */
+  SOS_ASSERT_FATAL(SOS_OK ==
+		   sos_thread_force_unblock(sleep_timeout_params
+					     ->thread_to_wakeup));
+}
+
+
+sos_ret_t sos_thread_sleep(struct sos_time *timeout)
+{
+  sos_ui32_t flags;
+  struct sleep_timeout_params sleep_timeout_params;
+  struct sos_timeout_action timeout_action;
+  sos_ret_t retval;
+
+  /* Block forever if no timeout is given */
+  if (NULL == timeout)
+    {
+      sos_disable_IRQs(flags);
+      retval = _switch_to_next_thread(BLOCK_MYSELF);
+      sos_restore_IRQs(flags);
+
+      return retval;
+    }
+
+  /* Initialize the timeout action */
+  sos_time_init_action(& timeout_action);
+
+  /* Prepare parameters used by the sleep timeout callback */
+  sleep_timeout_params.thread_to_wakeup 
+    = (struct sos_thread*)current_thread;
+  sleep_timeout_params.timeout_triggered = FALSE;
+
+  sos_disable_IRQs(flags);
+
+  /* Now program the timeout ! */
+  SOS_ASSERT_FATAL(SOS_OK ==
+		   sos_time_register_action_relative(& timeout_action,
+						     timeout,
+						     sleep_timeout,
+						     & sleep_timeout_params));
+
+  /* Prepare to block: wait for sleep_timeout() to wakeup us in the
+     timeout kwaitq, or for someone to wake us up in any other
+     waitq */
+  retval = _switch_to_next_thread(BLOCK_MYSELF);
+  /* Unblocked by something ! */
+
+  /* Unblocked by timeout ? */
+  if (sleep_timeout_params.timeout_triggered)
+    {
+      /* Yes */
+      SOS_ASSERT_FATAL(sos_time_is_zero(& timeout_action.timeout));
+      retval = SOS_OK;
+    }
+  else
+    {
+      /* No: We have probably been woken up while in some other
+	 kwaitq */
+      SOS_ASSERT_FATAL(SOS_OK == sos_time_unregister_action(& timeout_action));
+      retval = -SOS_EINTR;
+    }
+
+  sos_restore_IRQs(flags);
+
+  /* Update the remaining timeout */
+  memcpy(timeout, & timeout_action.timeout, sizeof(struct sos_time));
+
+  return retval;
+}
+
+
+sos_ret_t sos_thread_force_unblock(struct sos_thread *thread)
+{
+  sos_ret_t retval;
+  sos_ui32_t flags;
+
+  if (! thread)
+    return -SOS_EINVAL;
+  
+  sos_disable_IRQs(flags);
+
+  /* Thread already woken up ? */
+  retval = SOS_OK;
+  switch(sos_thread_get_state(thread))
+    {
+    case SOS_THR_RUNNING:
+    case SOS_THR_READY:
+      /* Do nothing */
+      break;
+
+    case SOS_THR_ZOMBIE:
+      retval = -SOS_EFATAL;
+      break;
+
+    default:
+      retval = sos_sched_set_ready(thread);
+      break;
+    }
+
+  sos_restore_IRQs(flags);
+
+  return retval;
+}
+
+
+void sos_thread_dump_backtrace(sos_bool_t on_console,
+			       sos_bool_t on_bochs)
+{
+  sos_vaddr_t stack_bottom = current_thread->kernel_stack_base_addr;
+  sos_size_t stack_size    = current_thread->kernel_stack_size;
+
+  static void backtracer(sos_vaddr_t PC,
+			 sos_vaddr_t params,
+			 sos_ui32_t depth,
+			 void *custom_arg)
+    {
+      sos_ui32_t invalid = 0xffffffff, *arg1, *arg2, *arg3, *arg4;
+
+      /* Get the address of the first 3 arguments from the
+	 frame. Among these arguments, 0, 1, 2, 3 arguments might be
+	 meaningful (depending on how many arguments the function may
+	 take). */
+      arg1 = (sos_ui32_t*)params;
+      arg2 = (sos_ui32_t*)(params+4);
+      arg3 = (sos_ui32_t*)(params+8);
+      arg4 = (sos_ui32_t*)(params+12);
+
+      /* Make sure the addresses of these arguments fit inside the
+	 stack boundaries */
+#define INTERVAL_OK(b,v,u) ( ((b) <= (sos_vaddr_t)(v)) \
+                             && ((sos_vaddr_t)(v) < (u)) )
+      if (!INTERVAL_OK(stack_bottom, arg1, stack_bottom + stack_size))
+	arg1 = &invalid;
+      if (!INTERVAL_OK(stack_bottom, arg2, stack_bottom + stack_size))
+	arg2 = &invalid;
+      if (!INTERVAL_OK(stack_bottom, arg3, stack_bottom + stack_size))
+	arg3 = &invalid;
+      if (!INTERVAL_OK(stack_bottom, arg4, stack_bottom + stack_size))
+	arg4 = &invalid;
+
+      /* Print the function context for this frame */
+      if (on_bochs)
+	sos_bochs_printf("[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x\n",
+			 (unsigned)depth, (unsigned)PC,
+			 (unsigned)*arg1, (unsigned)*arg2,
+			 (unsigned)*arg3);
+
+      if (on_console)
+	sos_x86_videomem_printf(23-depth, 3,
+				SOS_X86_VIDEO_BG_BLUE
+				  | SOS_X86_VIDEO_FG_LTGREEN,
+				"[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x arg4=0x%x",
+				(unsigned)depth, PC,
+				(unsigned)*arg1, (unsigned)*arg2,
+				(unsigned)*arg3, (unsigned)*arg4);
+      
+    }
+
+  sos_backtrace(NULL, 15, stack_bottom, stack_size,
+		backtracer, NULL);
+}
+
+
+
+/* **********************************************
+ * Restricted functions
+ */
+
+
+sos_ret_t
+sos_thread_change_current_mm_context(struct sos_mm_context *mm_ctxt)
+{
+  sos_ui32_t flags;
+
+  /* Retrieve the previous mm context */
+  struct sos_mm_context * prev_mm_ctxt
+    = current_thread->squatted_mm_context;
+
+  /* We should either select a new squatted_mm_context or revert to
+     the default */
+  if (mm_ctxt != NULL)
+    SOS_ASSERT_FATAL(prev_mm_ctxt == NULL);
+  else
+    SOS_ASSERT_FATAL(prev_mm_ctxt != NULL);
+
+  sos_disable_IRQs(flags);
+
+  /* Update current thread's squatted mm context */
+  current_thread->squatted_mm_context = mm_ctxt;
+
+  /* Update the reference counts and switch the MMU configuration if
+     needed */
+  if (mm_ctxt != NULL)
+    {
+      sos_mm_context_ref(mm_ctxt); /* Because it is now referenced as
+				      the squatted_mm_context field of
+				      the thread */
+      sos_mm_context_switch_to(mm_ctxt);
+    }
+  else
+    sos_mm_context_unref(prev_mm_ctxt); /* Because it is not referenced as
+					   the squatted_mm_context field of
+					   the thread any more */
+
+  sos_restore_IRQs(flags);
+
+  return SOS_OK;
+}
+
+
+void sos_thread_prepare_syscall_switch_back(struct sos_cpu_state *cpu_state)
+{
+  /* Don't preempt the current thread */
+
+  /*
+   * Save the state of the interrupted context to make sure that:
+   *   - The list of threads correctly reflects that the thread is back
+   *     in user mode
+   *   - _prepare_mm_context() deals with the correct mm_context
+   */
+  current_thread->cpu_state = cpu_state;
+
+  /* Perform an MMU context switch if needed */
+  _prepare_mm_context((struct sos_thread*) current_thread);
+}
+
+
+void sos_thread_prepare_exception_switch_back(struct sos_cpu_state *cpu_state)
+{
+  /* Don't preempt the current thread */
+
+  /*
+   * Save the state of the interrupted context to make sure that:
+   *   - The list of threads correctly reflects that the thread is
+   *     running in user or kernel mode
+   *   - _prepare_mm_context() deals with the correct mm_context
+   */
+  current_thread->cpu_state = cpu_state;
+
+  /* Perform an MMU context switch if needed */
+  _prepare_mm_context((struct sos_thread*) current_thread);
+}
+
+
+void
+sos_thread_prepare_irq_servicing(struct sos_cpu_state *interrupted_state)
+{
+  current_thread->cpu_state = interrupted_state;
+}
+
+
+struct sos_cpu_state *
+sos_thread_prepare_irq_switch_back(void)
+{
+  struct sos_thread *myself, *next_thread;
+
+  /* In SOS, threads in kernel mode are NEVER preempted from the
+     interrupt handlers ! */
+  if (! sos_cpu_context_is_in_user_mode(current_thread->cpu_state))
+    return current_thread->cpu_state;
+
+  /*
+   * Here we are dealing only with possible preemption of user threads
+   * in user context !
+   */
+
+  /* Make sure the thread actually is a user thread */
+  SOS_ASSERT_FATAL(current_thread->process != NULL);
+
+  /* Save the state of the interrupted context */
+  myself = (struct sos_thread*)current_thread;
+
+  /* Select the next thread to run */
+  next_thread = sos_reschedule(myself, FALSE);
+
+  /* Perform an MMU context switch if needed */
+  _prepare_mm_context(next_thread);
+
+  /* Setup the next_thread's context into the CPU */
+  _set_current(next_thread);
+  return next_thread->cpu_state;
+}
diff -ruN /tmp/sos-code-article6.75/sos/thread.h ../sos-code-article7/sos/thread.h
--- /tmp/sos-code-article6.75/sos/thread.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/thread.h	2005-04-27 20:14:21.000000000 +0200
@@ -0,0 +1,389 @@
+/* Copyright (C) 2004,2005 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_THREAD_H_
+#define _SOS_THREAD_H_
+
+/**
+ * @file thread.h
+ *
+ * SOS Thread management API
+ */
+
+#include <sos/errno.h>
+
+/* Forward declaration */
+struct sos_thread;
+
+#include <hwcore/cpu_context.h>
+#include <sos/sched.h>
+#include <sos/kwaitq.h>
+#include <sos/time.h>
+#include <sos/process.h>
+
+/**
+ * The possible states of a valid thread
+ */
+typedef enum { SOS_THR_CREATED, /**< Thread created, not fully initialized */
+	       SOS_THR_READY,   /**< Thread fully initialized or
+				     waiting for CPU after having been
+				     blocked or preempted */
+	       SOS_THR_RUNNING, /**< Thread currently running on CPU */
+	       SOS_THR_BLOCKED, /**< Thread waiting for I/O (+ in at LEAST
+				     one kwaitq) and/or sleeping (+ in NO
+				     kwaitq) */
+	       SOS_THR_ZOMBIE,  /**< Thread terminated execution, waiting to
+				     be deleted by kernel */
+             } sos_thread_state_t;
+
+
+/**
+ * TCB (Thread Control Block): structure describing a thread. Don't
+ * access these fields directly: prefer using the accessor functions
+ * below.
+ */
+struct sos_thread
+{
+#define SOS_THR_MAX_NAMELEN 32
+  char name[SOS_THR_MAX_NAMELEN];
+
+  sos_thread_state_t  state;
+  sos_sched_priority_t priority;
+
+  /**
+   * The hardware context of the thread.
+   *
+   * It will reflect the CPU state of the thread:
+   *  - From an interrupt handler: the state of the thread at the time
+   *    of the OUTERMOST irq. An IRQ is not allowed to make context
+   *    switches, so this context will remain valid from the begining of
+   *    the outermost IRQ handler to the end of it, no matter if there
+   *    are other IRQ handlers nesting in one another. You may safely
+   *    use it from IRQ handlers to query the state of the interrupted
+   *    thread, no matter if there has been other IRQ handlers
+   *    executing meanwhile.
+   *  - From normal kernel code, exceptions and syscall: the state of
+   *    the thread the last time there was a context switch from this
+   *    thread to another one. Thus this field WON'T reflect the
+   *    current's thread cpu_state in these cases. So, in these cases,
+   *    simply DO NOT USE IT outside thread.c ! Note: for syscall and
+   *    exception handlers, the VALID state of the interrupted thread is
+   *    passed as an argument to the handlers.
+   */
+  struct sos_cpu_state *cpu_state;
+
+  /* Kernel stack parameters */
+  sos_vaddr_t kernel_stack_base_addr;
+  sos_size_t  kernel_stack_size;
+
+  /* Process this thread belongs to. Always NULL for a kernel
+     thread */
+  struct sos_process *process;
+
+  /**
+   * Address space currently "squatted" by the thread, or used to be
+   * active when the thread was interrupted/preempted. This is the MMU
+   * configuration expected before the cpu_state of the thread is
+   * restored on CPU.
+   *   - For kernel threads: should normally be NULL, meaning that the
+   *     thread will squat the current mm_context currently set in the
+   *     MMU. Might be NON NULL when a kernel thread squats a given
+   *     process to manipulate its address space.
+   *   - For user threads: should normally be NULL. More precisely:
+   *       - in user mode: the thread->process.mm_context is ALWAYS
+   *         set on MMU. squatted_mm_context is ALWAYS NULL in this
+   *         situation, meaning that the thread in user mode uses its
+   *         process-space as expected
+   *       - in kernel mode: NULL means that we keep on using the
+   *         mm_context currently set on MMU, which might be the
+   *         mm_context of another process. This is natural since a
+   *         thread in kernel mode normally only uses data in kernel
+   *         space. BTW, this limits the number of TLB flushes. However,
+   *         there are exceptions where this squatted_mm_context will
+   *         NOT be NULL. One is the copy_from/to_user API, which can
+   *         force the effective mm_context so that the MMU will be
+   *         (re)configured upon every context to the thread to match
+   *         the squatted_mm_context. Another exception is when a parent
+   *         thread creates the address space of a child process, in
+   *         which case the parent thread might temporarilly decide to
+   *         switch to the child's process space.
+   *
+   * This is the SOS implementation of the Linux "Lazy TLB" and
+   * address-space loaning.
+   */
+  struct sos_mm_context *squatted_mm_context;
+
+  /* Data specific to each state */
+  union
+  {
+    struct
+    {
+      struct sos_sched_queue *rdy_queue;
+      struct sos_thread     *rdy_prev, *rdy_next;
+    } ready;
+
+    struct
+    {
+      struct sos_time user_time_spent_in_slice;
+    } running;
+  }; /* Anonymous union (gcc extenion) */
+
+
+  /*
+   * Data used by the kwaitq subsystem: list of kwaitqueues the thread
+   * is waiting for.
+   *
+   * @note: a RUNNING or READY thread might be in one or more
+   * waitqueues ! The only property we have is that, among these
+   * waitqueues (if any), _at least_ one has woken the thread.
+   */
+  struct sos_kwaitq_entry *kwaitq_list;
+
+
+  /**
+   * Some statistics
+   */
+  struct rusage
+  {
+    /* Updated by sched.c */
+    struct sos_time ru_utime; /* Time spent in user mode */
+    struct sos_time ru_stime; /* Time spent in kernel mode */
+  } rusage;
+
+
+  /**
+   * Chaining pointers for the list of threads in the parent process
+   */
+  struct sos_thread *prev_in_process, *next_in_process;
+
+
+  /**
+   * Chaining pointers for global ("gbl") list of threads (debug)
+   */
+  struct sos_thread *gbl_prev, *gbl_next;
+};
+
+
+/**
+ * Definition of the function executed by a kernel thread
+ */
+typedef void (*sos_kernel_thread_start_routine_t)(void *arg);
+
+
+/**
+ * Initialize the subsystem responsible for thread management
+ *
+ * Initialize the primary kernel thread so that it can be handled the
+ * same way as an ordinary thread created by sos_thread_create().
+ */
+sos_ret_t sos_thread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
+				     sos_size_t init_thread_stack_size);
+
+
+/**
+ * Create a new kernel thread
+ */
+struct sos_thread *
+sos_create_kernel_thread(const char *name,
+			 sos_kernel_thread_start_routine_t start_func,
+			 void *start_arg,
+			 sos_sched_priority_t priority);
+
+
+/**
+ * Create a new user thread
+ */
+struct sos_thread *
+sos_create_user_thread(const char *name,
+		       struct sos_process *process,
+		       sos_uaddr_t user_initial_PC,
+		       sos_ui32_t  user_start_arg1,
+		       sos_ui32_t  user_start_arg2,
+		       sos_uaddr_t user_initial_SP,
+		       sos_sched_priority_t priority);
+
+
+/**
+ * Terminate the execution of the current thread. For kernel threads,
+ * it is called by default when the start routine returns.
+ */
+void sos_thread_exit() __attribute__((noreturn));
+
+
+/**
+ * Get the identifier of the thread currently running on CPU. Trivial
+ * function.
+ */
+struct sos_thread *sos_thread_get_current();
+
+
+/**
+ * If thr == NULL, set the priority of the current thread. Trivial
+ * function.
+ *
+ * @note NOT protected against interrupts
+ */
+sos_sched_priority_t sos_thread_get_priority(struct sos_thread *thr);
+
+
+/**
+ * If thr == NULL, get the state of the current thread. Trivial
+ * function.
+ *
+ * @note NOT protected against interrupts
+ */
+sos_thread_state_t sos_thread_get_state(struct sos_thread *thr);
+
+
+/**
+ * If thr == NULL, set the priority of the current thread
+ *
+ * @note NO context-switch ever occurs in this function !
+ */
+sos_ret_t sos_thread_set_priority(struct sos_thread *thr,
+				  sos_sched_priority_t priority);
+
+
+/**
+ * Yield CPU to another ready thread.
+ *
+ * @note This is a BLOCKING FUNCTION
+ */
+sos_ret_t sos_thread_yield();
+
+
+/**
+ * Release the CPU for (at least) the given delay.
+ *
+ * @param delay The delay to wait for. If delay == NULL then wait
+ * forever that any event occurs.
+ *
+ * @return SOS_OK when delay expired (and delay is reset to zero),
+ * -SOS_EINTR otherwise (and delay contains the amount of time
+ * remaining).
+ *
+ * @note This is a BLOCKING FUNCTION
+ */
+sos_ret_t sos_thread_sleep(/* in/out */struct sos_time *delay);
+
+
+/**
+ * Mark the given thread as READY (if not already ready) even if it is
+ * blocked in a kwaitq or in a sleep ! As a result, the interrupted
+ * kwaitq/sleep function call of the thread will return with
+ * -SOS_EINTR.
+ *
+ * @return -SOS_EINVAL if thread does not exist, or -SOS_EFATAL if
+ * marked ZOMBIE.
+ *
+ * @note As a result, the semaphore/mutex/conditions/... functions
+ * return values SHOULD ALWAYS be checked ! If they are != SOS_OK,
+ * then the caller should consider that the resource is not aquired
+ * because somebody woke the thread by some way.
+ */
+sos_ret_t sos_thread_force_unblock(struct sos_thread *thread);
+
+/**
+ * Dump the backtrace of the current thread to console and/or bochs
+ */
+void sos_thread_dump_backtrace(sos_bool_t on_console,
+			       sos_bool_t on_bochs);
+
+
+/* **********************************************
+ * Restricted functions
+ */
+
+
+/**
+ * Restricted function to change the current mm_context AND the
+ * squatted_mm_context of the current thread in order to access the data
+ * in this context
+ *
+ *   @param mm_ctxt The mm_ctxt to restore. Might be NULL, meaning that:
+ *    - for a Kernel thread: the current MMU configuration is never
+ *      modified. The address space to use is limited to the kernel
+ *      space, user space might change due to preemptions to other
+ *      processes
+ *    - for a User thread in kernel mode: same as for kernel threads
+ *    - when a User thread will go back in user context: the MMU will
+ *      be reconfigured to match the mm_context of the thread's
+ *      process
+ *
+ * @note A non NULL parameter is allowed only if the
+ * squatted_mm_context is not already set. A NULL parameter is allowed
+ * only if the squatted_mm_context was already set.
+ *
+ * @note The use of this function is RESERVED to the syscall handler
+ * and the copy_from/to_user functions
+ */
+sos_ret_t
+sos_thread_change_current_mm_context(struct sos_mm_context *mm_ctxt);
+
+
+/**
+ * Restricted callback called when a syscall goes back in user mode,
+ * to reconfigure the MMU to match that of the current thread's
+ * process MMU context.
+ *
+ * @note The use of this function is RESERVED to the syscall wrapper
+ */
+void sos_thread_prepare_syscall_switch_back(struct sos_cpu_state *cpu_state);
+
+
+/**
+ * Restricted callback called when an exception handler goes back to
+ * the interrupted thread to reconfigure the MMU to match that of the
+ * current thread's process MMU context.
+ *
+ * @note The use of this function is RESERVED to the exception wrappers
+ */
+void sos_thread_prepare_exception_switch_back(struct sos_cpu_state *cpu_state);
+
+
+/**
+ * Restricted callback called when an IRQ is entered while the CPU was
+ * NOT already servicing any other IRQ (ie the outermost IRQ handler
+ * is entered). This callback simply updates the "cpu_state" field so
+ * that IRQ handlers always know the state of the interrupted thread,
+ * even if they are imbricated in other IRQ handlers.
+ *
+ * @note The use of this function is RESERVED to the irq wrappers
+ */
+void
+sos_thread_prepare_irq_servicing(struct sos_cpu_state *interrupted_state);
+
+
+/**
+ * Restricted callback called when the outermost IRQ handler returns,
+ * to select the thread to return to. This callbacks implements:
+ *   - preemption of user threads in user mode (time sharing / FIFO)
+ *   - non-preemption of user threads in kernel mode (interrupted thread
+ *     is restored on CPU "as is")
+ *   - non-preemption of kernel threads (same remark)
+ * The MMU is reconfigured correctly to match the address space of the
+ * selected thread.
+ *
+ * @return The CPU context of the thread to return to
+ *
+ * @note The use of this function is RESERVED to the irq wrappers
+ */
+struct sos_cpu_state *
+sos_thread_prepare_irq_switch_back(void);
+
+
+#endif /* _SOS_THREAD_H_ */
diff -ruN /tmp/sos-code-article6.75/sos/time.c ../sos-code-article7/sos/time.c
--- /tmp/sos-code-article6.75/sos/time.c	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/time.c	2005-04-27 20:14:21.000000000 +0200
@@ -20,7 +20,6 @@
 #include <sos/klibc.h>
 #include <hwcore/irq.h>
 #include <sos/list.h>
-#include <sos/kthread.h>
 
 #include "time.h"
 
@@ -151,11 +150,11 @@
 sos_ret_t sos_time_set_tick_resolution(const struct sos_time *resolution)
 {
   sos_ui32_t flags;
-  sos_disable_IRQs(flags);
 
+  sos_disable_IRQs(flags);
   memcpy(& tick_resolution, resolution, sizeof(struct sos_time));
-
   sos_restore_IRQs(flags);
+
   return SOS_OK;
 }
 
diff -ruN /tmp/sos-code-article6.75/sos/types.h ../sos-code-article7/sos/types.h
--- /tmp/sos-code-article6.75/sos/types.h	2005-01-04 04:13:53.000000000 +0100
+++ ../sos-code-article7/sos/types.h	2005-04-27 20:14:21.000000000 +0200
@@ -1,5 +1,4 @@
 /* Copyright (C) 2004  The SOS Team
-   Copyright (C) 1999  Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -26,23 +25,38 @@
  */
 
 /** Physical address */
-typedef unsigned int       sos_paddr_t;
+typedef unsigned int           sos_paddr_t;
 
-/** Generic virtual address (kernel or user) */
-typedef unsigned int       sos_vaddr_t;
+/** Kernel virtual address */
+typedef unsigned int           sos_vaddr_t;
+
+/** User virtual address */
+typedef unsigned int           sos_uaddr_t;
 
 /** Memory size of an object (positive) */
-typedef unsigned int       sos_size_t;
+typedef unsigned int           sos_size_t;
+/** Generic positive offset into objects */
+typedef unsigned int           sos_uoffset_t;
+/** Generic signed offset into objects */
+typedef signed int             sos_soffset_t;
+/** Generic positive LONG (64 bits) offset into objects */
+typedef unsigned long long int sos_luoffset_t;
+/** Generic signed LONG (64 bits) offset into objects */
+typedef signed long long int   sos_lsoffset_t;
+
 /** Generic count of objects */
-typedef unsigned int       sos_count_t;
+typedef unsigned int           sos_count_t;
 
-/** Low-level sizes */
-typedef unsigned long int  sos_ui32_t; /* 32b unsigned */
-typedef unsigned short int sos_ui16_t; /* 16b unsigned */
-typedef unsigned char      sos_ui8_t;  /* 8b unsigned */
-typedef signed long int    sos_si32_t; /* 32b signed */
-typedef signed short int   sos_si16_t; /* 16b signed */
-typedef signed char        sos_si8_t;  /* 8b signed */
+/* Low-level sizes */
+typedef unsigned long long int sos_ui64_t; /**< 32b unsigned */
+typedef unsigned long int      sos_ui32_t; /**< 32b unsigned */
+typedef unsigned short int     sos_ui16_t; /**< 16b unsigned */
+typedef unsigned char          sos_ui8_t;  /**< 8b unsigned */
+
+typedef signed long long int   sos_si64_t; /**< 64b signed */
+typedef signed long int        sos_si32_t; /**< 32b signed */
+typedef signed short int       sos_si16_t; /**< 16b signed */
+typedef signed char            sos_si8_t;  /**< 8b signed */
 
 typedef enum { FALSE=0, TRUE } sos_bool_t;
 
diff -ruN /tmp/sos-code-article6.75/sos/uaccess.c ../sos-code-article7/sos/uaccess.c
--- /tmp/sos-code-article6.75/sos/uaccess.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/uaccess.c	2005-04-27 20:14:21.000000000 +0200
@@ -0,0 +1,200 @@
+/* Copyright (C) 2005  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 <hwcore/paging.h>
+#include <sos/thread.h>
+#include <sos/assert.h>
+#include <sos/kmalloc.h>
+
+#include "uaccess.h"
+
+
+/**
+ * Helper function to force the MMU to switch to the address space of
+ * the process
+ */
+static sos_ret_t bindto_user_mm_context()
+{
+  struct sos_thread * current_thread;
+  struct sos_mm_context * process_mm_ctxt;
+
+  current_thread = sos_thread_get_current();
+  SOS_ASSERT_FATAL(NULL != current_thread->process);
+
+  /* Switch to the user's address space */
+  process_mm_ctxt
+    = sos_process_get_mm_context(current_thread->process);
+  return sos_thread_change_current_mm_context(process_mm_ctxt);
+}
+
+
+/**
+ * Helper macro to restore the MMU configuration prior to
+ * bindto_user_mm_context()
+ */
+#define unbindto_user_mm_context() \
+   sos_thread_change_current_mm_context(NULL)
+
+
+static sos_ret_t nocheck_user_memcpy(sos_vaddr_t dest,
+				     sos_vaddr_t src,
+				     sos_size_t size,
+				     sos_bool_t transfer_from_user)
+{
+  sos_ret_t retval;
+
+  retval = bindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  memcpy((void*) dest, (void*) src, size);
+
+  retval = unbindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  return size;
+}
+
+
+sos_ret_t sos_memcpy_from_user(sos_vaddr_t kernel_to,
+			       sos_uaddr_t user_from,
+			       sos_size_t size)
+{
+  /* Make sure user is trying to access user space */
+  if (user_from < SOS_PAGING_BASE_USER_ADDRESS)
+    return -SOS_EPERM;
+
+  if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size)
+    return -SOS_EPERM;
+
+  return nocheck_user_memcpy(kernel_to, user_from, size, TRUE);
+}
+
+
+sos_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
+			     sos_vaddr_t kernel_from,
+			     sos_size_t size)
+{
+  /* Make sure user is trying to access user space */
+  if (user_to < SOS_PAGING_BASE_USER_ADDRESS)
+    return -SOS_EPERM;
+
+  if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size)
+    return -SOS_EPERM;
+
+  return nocheck_user_memcpy(user_to, kernel_from, size, FALSE);
+}
+
+
+sos_ret_t sos_strnlen_from_user(sos_uaddr_t user_str, sos_size_t max_len)
+{
+  sos_ret_t retval, len;
+
+  /* Make sure user is trying to access user space */
+  if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
+    return -SOS_EPERM;
+
+  /* Don't allow invalid max_len */
+  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
+    return -SOS_EINVAL;
+
+  retval = bindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  len = strnlen((const char*)user_str, max_len);
+
+  retval = unbindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  return len;
+}
+
+
+static sos_ret_t nocheck_user_strzcpy(char *dst, const char *src,
+				      sos_size_t len)
+{
+  sos_ret_t retval;
+
+  retval = bindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  strzcpy(dst, src, len);
+
+  retval = unbindto_user_mm_context();
+  if (SOS_OK != retval)
+    return retval;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
+				sos_size_t max_len)
+{
+  /* Make sure user is trying to access user space */
+  if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS)
+    return -SOS_EPERM;
+
+  /* Don't allow invalid max_len */
+  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
+    return -SOS_EINVAL;
+
+  return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);
+}
+
+
+sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
+			      sos_size_t max_len)
+{
+  /* Make sure user is trying to access user space */
+  if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS)
+    return -SOS_EPERM;
+
+  /* Don't allow invalid max_len */
+  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
+    return -SOS_EINVAL;
+
+  return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);
+}
+
+
+sos_ret_t sos_strndup_from_user(char ** kernel_to, sos_uaddr_t from_user,
+				sos_size_t max_len,
+				sos_ui32_t kmalloc_flags)
+{
+  sos_ret_t retval = sos_strnlen_from_user(from_user, max_len);
+  if (retval < 0)
+    return retval;
+
+  *kernel_to = (char*)sos_kmalloc(retval + 1, kmalloc_flags);
+  if (NULL == *kernel_to)
+    return -SOS_ENOMEM;
+
+  retval = sos_strzcpy_from_user(*kernel_to, from_user, retval + 1);
+  if (SOS_OK != retval)
+    {
+      sos_kfree((sos_vaddr_t)*kernel_to);
+      *kernel_to = NULL;
+    }
+
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/sos/uaccess.h ../sos-code-article7/sos/uaccess.h
--- /tmp/sos-code-article6.75/sos/uaccess.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/sos/uaccess.h	2005-04-27 20:14:21.000000000 +0200
@@ -0,0 +1,96 @@
+/* Copyright (C) 2005  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_UACCESS_H_
+#define _SOS_UACCESS_H_
+
+
+/**
+ * @file uaccess.h
+ *
+ * Routines to access user-space data from inside the kernel space.
+ */
+
+#include <sos/types.h>
+#include <sos/errno.h>
+
+
+/**
+ * Retrieve a bunch of data from the user space of the
+ * current_thread->process
+ *
+ * @return <0 on error ! Return the number of bytes successfully copied
+ * otherwise.
+ */
+sos_ret_t sos_memcpy_from_user(sos_vaddr_t kernel_to,
+			       sos_uaddr_t user_from,
+			       sos_size_t size);
+
+
+/**
+ * Transfer a bunch of data to the user space of the
+ * current_thread->process
+ *
+ * @return <0 n error ! Return the number of bytes successfully copied
+ * otherwise.
+ */
+sos_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
+			     sos_vaddr_t kernel_from,
+			     sos_size_t size);
+
+
+/**
+ * @return the length of the given user space string user_str
+ * (excluding the trailing \0), up to max_len bytes. <0 on error
+ * (unresolved page fault, etc.)
+ */
+sos_ret_t sos_strnlen_from_user(sos_uaddr_t user_str, sos_size_t max_len);
+
+
+/**
+ * Copy the given user space string to kernel space, up to max_len
+ * bytes (including trailing \0)
+ *
+ * @return SOS_OK on success, <0 otherwise (unresolved page fault, etc.)
+ */
+sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
+				sos_size_t max_len);
+
+
+/**
+ * Copy the given kernel string to user space, up to max_len bytes
+ * (including trailing \0)
+ *
+ * @return SOS_OK on success, <0 otherwise (unresolved page fault, etc.)
+ */
+sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
+			      sos_size_t max_len);
+
+
+/**
+ * Copy the given user space string into a new allocated kernel space
+ * area of the correct size, up to max_len bytes (including trailing
+ * \0)
+ *
+ * @return SOS_OK on success + *kernel_to is set to the freshly
+ * allocated kernel string, <0 otherwise (unresolved page fault, etc.)
+ */
+sos_ret_t sos_strndup_from_user(char ** kernel_to, sos_uaddr_t from_user,
+				sos_size_t max_len,
+				sos_ui32_t kmalloc_flags);
+
+#endif /* _SOS_UACCESS_H_ */
diff -ruN /tmp/sos-code-article6.75/support/sos.lds ../sos-code-article7/support/sos.lds
--- /tmp/sos-code-article6.75/support/sos.lds	2005-01-04 04:13:54.000000000 +0100
+++ ../sos-code-article7/support/sos.lds	2005-04-27 20:14:22.000000000 +0200
@@ -76,9 +76,20 @@
     /* Beginning of the read-only data section */
     .rodata . :
     {   *(.rodata*)
+	*(.eh_frame*)
+
+	/* For articles 7.5 and later, it is better if the program
+           "files" are located on a 4kB boundary: this allows
+           binfmt_elf32 to share program pages between kernel and
+           user, alleviating the need to allocate new pages to copy the
+           user code */
+	. = ALIGN(4096);
+	*(.userprogs)
+
         PROVIDE(erodata = .);
         PROVIDE(_erodata = .);
     }
+
     /* We take note of the end of the data to load */
     __e_load = .;
 
diff -ruN /tmp/sos-code-article6.75/userland/crt.c ../sos-code-article7/userland/crt.c
--- /tmp/sos-code-article6.75/userland/crt.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/crt.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,183 @@
+/* Copyright (C) 2005 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. 
+*/
+
+
+/**
+ * @file crt.c
+ *
+ * The C RunTime environment for the basic support of SOS C user
+ * programs
+ */
+
+#include <hwcore/swintr.h>
+#include <string.h>
+#include "crt.h"
+
+/**
+ * Starter function !
+ */
+void _start() __attribute__((noreturn));
+void _start()
+{
+  /* This starter function expects a main() function somewhere */
+  extern int main();
+
+  /* Reset the bss section */
+  extern char _bbss, _ebss;
+  memset(& _bbss, 0x0, (& _ebss) - (& _bbss));
+
+  _sos_exit(main());
+}
+
+
+/*
+ * By convention, the USER SOS programs always pass 4 arguments to the
+ * kernel syscall handler: in eax/../edx. For less arguments, the
+ * unused registers are filled with 0s. For more arguments, the 4th
+ * syscall parameter gives the address of the array containing the
+ * remaining arguments. In any case, eax corresponds to the syscall
+ * IDentifier.
+ */
+
+
+inline
+int _sos_syscall3(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3)
+{
+  int ret;
+
+  asm volatile("movl %1,%%eax \n"
+	       "movl %2,%%ebx \n"
+	       "movl %3,%%ecx \n"
+	       "movl %4,%%edx \n"
+	       "int  %5\n"
+	       "movl %%eax, %0"
+	       :"=g"(ret)
+	       :"g"(id),"g"(arg1),"g"(arg2),"g"(arg3)
+	        ,"i"(SOS_SWINTR_SOS_SYSCALL)
+	       :"eax","ebx","ecx","edx");
+
+  return ret;
+}
+
+
+int _sos_syscall0(int id)
+{
+  return _sos_syscall3(id, 0, 0, 0);
+}
+
+
+int _sos_syscall1(int id,
+	     unsigned int arg1)
+{
+  return _sos_syscall3(id, arg1, 0, 0);
+}
+
+
+int _sos_syscall2(int id,
+		  unsigned int arg1,
+		  unsigned int arg2)
+{
+  return _sos_syscall3(id, arg1, arg2, 0);
+}
+
+
+int _sos_syscall4(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4)
+{
+  unsigned int args[] = { arg3, arg4 };
+  return _sos_syscall3(id, arg1, arg2, (unsigned)args);
+}
+
+
+int _sos_syscall5(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5)
+{
+  unsigned int args[] = { arg3, arg4, arg5 };
+  return _sos_syscall3(id, arg1, arg2, (unsigned)args);
+}
+
+
+int _sos_syscall6(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6)
+{
+  unsigned int args[] = { arg3, arg4, arg5, arg6 };
+  return _sos_syscall3(id, arg1, arg2, (unsigned)args);
+}
+
+
+int _sos_syscall7(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6,
+		  unsigned int arg7)
+{
+  unsigned int args[] = { arg3, arg4, arg5, arg6, arg7 };
+  return _sos_syscall3(id, arg1, arg2, (unsigned)args);
+}
+
+
+int _sos_syscall8(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6,
+		  unsigned int arg7,
+		  unsigned int arg8)
+{
+  unsigned int args[] = { arg3, arg4, arg5, arg6, arg7, arg8 };
+  return _sos_syscall3(id, arg1, arg2, (unsigned)args);
+}
+
+
+void _sos_exit(int status)
+{
+  _sos_syscall1(SOS_SYSCALL_ID_EXIT, (unsigned)status);
+  
+  /* Never reached ! */
+  for ( ; ; )
+    ;
+}
+
+
+int _sos_bochs_write(const char * str, unsigned length)
+{
+  return _sos_syscall2(SOS_SYSCALL_ID_BOCHS_WRITE,
+		       (unsigned)str,
+		       length);
+}
diff -ruN /tmp/sos-code-article6.75/userland/crt.h ../sos-code-article7/userland/crt.h
--- /tmp/sos-code-article6.75/userland/crt.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/crt.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,107 @@
+/* Copyright (C) 2005 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. 
+*/
+#ifndef _SOS_USER_CRT_H_
+#define _SOS_USER_CRT_H_
+
+
+/**
+ * @file crt.h
+ *
+ * C runtime environment for the user side of the kernel/user
+ * interface
+ */
+
+#include <types.h>
+
+/**
+ * We include the syscall.h file from the kernel to retrieve the list
+ * of available syscall ID
+ */
+#include <sos/syscall.h>
+
+
+/**
+ * The syscall wrappers hiding the details of the implementation
+ */
+int _sos_syscall0(int id);
+
+int _sos_syscall1(int id,
+		  unsigned int arg1);
+
+int _sos_syscall2(int id,
+		  unsigned int arg1,
+		  unsigned int arg2);
+
+int _sos_syscall3(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3);
+
+int _sos_syscall4(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4);
+
+int _sos_syscall5(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5);
+
+int _sos_syscall6(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6);
+
+int _sos_syscall7(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6,
+		  unsigned int arg7);
+
+int _sos_syscall8(int id,
+		  unsigned int arg1,
+		  unsigned int arg2,
+		  unsigned int arg3,
+		  unsigned int arg4,
+		  unsigned int arg5,
+		  unsigned int arg6,
+		  unsigned int arg7,
+		  unsigned int arg8);
+
+
+/**
+ * The most important function of a C program ! ;)
+ */
+void _sos_exit (int status) __attribute__((noreturn));
+
+
+/**
+ * Non standard function to print something on bochs
+ */
+int _sos_bochs_write(const char *str, unsigned length);
+#endif /* _SOS_USER_CRT_H_ */
diff -ruN /tmp/sos-code-article6.75/userland/debug.c ../sos-code-article7/userland/debug.c
--- /tmp/sos-code-article6.75/userland/debug.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/debug.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,39 @@
+/* Copyright (C) 2004  David Decotigny (with INSA Rennes for vsnprintf)
+   Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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 <crt.h>
+#include <stdarg.h>
+
+#include "debug.h"
+
+int bochs_printf(const char *format, /* args */...)
+{
+  char buff[4096];
+  int len;
+  va_list ap;
+  
+  va_start(ap, format);
+  len = vsnprintf(buff, sizeof(buff), format, ap);
+  va_end(ap);
+  
+  if (len < 0)
+    return len;
+
+  return _sos_bochs_write(buff, len);
+}
diff -ruN /tmp/sos-code-article6.75/userland/debug.h ../sos-code-article7/userland/debug.h
--- /tmp/sos-code-article6.75/userland/debug.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/debug.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,34 @@
+/* Copyright (C) 2005 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. 
+*/
+#ifndef _SOS_LIBC_DEBUG_H_
+#define _SOS_LIBC_DEBUG_H_
+
+/**
+ * @file debug.h
+ *
+ * Non standard debugging features
+ */
+
+/**
+ * Non standard function to print something on bochs
+ */
+int bochs_printf(const char *format, /* args */...)
+     __attribute__ ((format (printf, 1, 2)));
+
+#endif /* _SOS_USER_LIBC_H_ */
diff -ruN /tmp/sos-code-article6.75/userland/ldscript.lds ../sos-code-article7/userland/ldscript.lds
--- /tmp/sos-code-article6.75/userland/ldscript.lds	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/ldscript.lds	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,67 @@
+/* Copyright (C) 2005, 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. 
+*/
+
+/* We generate binary in the ELF format */
+OUTPUT_FORMAT("elf32-i386","elf32-i386","elf32-i386");
+
+/* The entry point of the program is _start (defined in crt.c) */
+ENTRY(_start)
+
+/* The architecture is i386 */
+OUTPUT_ARCH("i386")
+
+SECTIONS
+{
+    /* our program is loaded at 2G */
+    . = 0x80000000;
+
+    /* Beginning of the text section */
+    .text :
+    {   
+	/* This section includes the code */
+        *(.text*)
+	/* Defines the 'etext' and '_etext' at the end */
+        PROVIDE(etext = .);
+        PROVIDE(_etext = .);
+    }
+
+    /* Beginning of the data section */
+    .data . :
+    {   *(.data*) 
+        PROVIDE(edata = .);
+	PROVIDE(_edata = .);
+    }
+
+    /* Beginning of the read-only data section */
+    .rodata . :
+    {   *(.rodata*)
+	*(.eh_frame*)
+        PROVIDE(erodata = .);
+        PROVIDE(_erodata = .);
+    }
+
+    /* Beginning of the BSS section (global uninitialized data) */
+    .bss SIZEOF(.rodata) + ADDR(.rodata) :
+    {   PROVIDE(bbss = .);
+	PROVIDE(_bbss = .);
+	*(.bss)
+        *(COMMON)
+        PROVIDE(ebss = .);
+	PROVIDE(_ebss = .);
+    }
+}
diff -ruN /tmp/sos-code-article6.75/userland/libc.c ../sos-code-article7/userland/libc.c
--- /tmp/sos-code-article6.75/userland/libc.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/libc.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,17 @@
+/* Copyright (C) 2005 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. 
+*/
diff -ruN /tmp/sos-code-article6.75/userland/libc.h ../sos-code-article7/userland/libc.h
--- /tmp/sos-code-article6.75/userland/libc.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/libc.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,35 @@
+/* Copyright (C) 2005 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. 
+*/
+#ifndef _SOS_USER_LIBC_H_
+#define _SOS_USER_LIBC_H_
+
+#include <types.h>
+
+/**
+ * @file libc.h
+ *
+ * The basic user C library for user programs
+ */
+
+/**
+ * The most important function of a C program ! ;)
+ */
+void exit (int status) __attribute__((noreturn, alias("_sos_exit")));
+
+#endif /* _SOS_USER_LIBC_H_ */
diff -ruN /tmp/sos-code-article6.75/userland/Makefile ../sos-code-article7/userland/Makefile
--- /tmp/sos-code-article6.75/userland/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/Makefile	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,89 @@
+## Copyright (C) 2005  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. 
+
+CC=gcc
+AR=ar
+OBJCOPY=objcopy
+CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I..
+LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
+LDFLAGS = -Wl,--warn-common -nostdlib -Wl,-Tldscript.lds
+
+# Main target
+all: userprogs.kimg
+
+-include .mkvars
+
+PROGS := myprog1 myprog2 myprog3 myprog4 myprog5 myprog6
+
+# Build dependencies of the programs
+$(PROGS) : % : %.o crt.o libc.a
+
+PWD := $(shell pwd)
+
+# Programs generation
+$(PROGS):
+	$(CC) -static $(LDFLAGS) -o $@ $^ $(LIBGCC)
+
+# Generation of the libC
+libc.a: libc.o string.o stdarg.o debug.o
+
+# Create a program image to be integrated into the Kernel
+userprogs.kimg: $(PROGS)
+	@echo "# Generating ELF images for inclusion into the kernel image: $@"
+	@echo "SECTIONS { .userprogs . : { " > .userprogs.lds
+	@i=0 ;                                                              \
+	 for f in $^ ; do                                                   \
+           i=`expr $$i + 1` ;                                               \
+	   echo "extern char _begin_userprog$$i, _end_userprog$$i;"         \
+                > .userprog$$i.c ;                                          \
+	   echo "char *_userprog"$$i"_entry[]" >> .userprog$$i.c ;          \
+	   echo "  __attribute__((section(\".userprogs_table\")))"          \
+                >> .userprog$$i.c ;                                         \
+	   echo "  = { \"$$f\", &_begin_userprog$$i, &_end_userprog$$i };"  \
+                >> .userprog$$i.c ;                                         \
+	   $(CC) $(CFLAGS) -c .userprog$$i.c -o .userprog$$i.o ;            \
+           $(OBJCOPY) --add-section .userprog$$i=$$f .userprog$$i.o         \
+                .userprog$$i.kimg ;                                         \
+           echo "  . = ALIGN(4096);" >> .userprogs.lds ;                    \
+           echo "  _begin_userprog$$i = .;" >> .userprogs.lds ;             \
+	   echo "  .userprog$$i.kimg(.userprog$$i);" >> .userprogs.lds ;    \
+	   echo "  _end_userprog$$i = .;" >> .userprogs.lds ;               \
+	   echo "  .userprog$$i.kimg(.rodata); .userprog$$i.kimg(.data);"   \
+                >> .userprogs.lds ;                                         \
+         done
+	@echo "  _userprogs_table = .; *(.userprogs_table) ; LONG(0);"      \
+                >> .userprogs.lds
+	@echo "} /DISCARD/ : { *(.text) *(.data) *(.bss) } }"               \
+                >> .userprogs.lds
+	@$(LD) -r -o $@ -T.userprogs.lds
+
+# Create libraries from object files
+%.a:
+	$(AR) rcv $@ $^
+
+# Create objects from C source code
+%.o: %.c
+	$(CC) -I$(PWD) -c $< $(CFLAGS) -o $@
+
+# Create objects from assembler (.S) source code
+%.o: %.S
+	$(CC) -I$(PWD) -c $< $(CFLAGS) -DASM_SOURCE=1 -o $@
+
+# Clean directory
+clean:
+	$(RM) *.o *.a *~ $(PROGS) *.kimg
+	$(RM) .userprog*
diff -ruN /tmp/sos-code-article6.75/userland/myprog1.c ../sos-code-article7/userland/myprog1.c
--- /tmp/sos-code-article6.75/userland/myprog1.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog1.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,37 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog1.c
+ * Basic print/exit test
+ */
+
+int main()
+{
+  int retval;
+
+  bochs_printf("Prog1: Hello world 1 !\n");
+  bochs_printf("Prog1: Hello world 2 !\n");
+  bochs_printf("Prog1: Hello world 3 !\n");
+  bochs_printf("Prog1: Hello world 4 !\n");
+  retval = bochs_printf("Prog1: Hello world 5 !\n");
+
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/userland/myprog2.c ../sos-code-article7/userland/myprog2.c
--- /tmp/sos-code-article6.75/userland/myprog2.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog2.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,35 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog2.c
+ * Basic print/pause/exit test
+ */
+
+int main()
+{
+  int i;
+  bochs_printf("Prog2: Hello world A !\n");
+  for (i = 0 ; i < 10000000 ; i++)
+    continue;
+  bochs_printf("Prog2: Hello world B !\n");
+
+  return 0;
+}
diff -ruN /tmp/sos-code-article6.75/userland/myprog3.c ../sos-code-article7/userland/myprog3.c
--- /tmp/sos-code-article6.75/userland/myprog3.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog3.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,46 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog3.c
+ * Test unresolved page fault (write to kernel space)
+ */
+
+int main()
+{
+  char *c;
+  int i;
+  bochs_printf("Prog3: Hello world A !\n");
+  for (i = 0 ; i < 100000 ; i++)
+    continue;
+  bochs_printf("Prog3: Hello world B !\n");
+
+  /* Force a page fault */
+  c = (char*) 0x201000;
+  /* Try to read somewhere in the kernel space */
+  i = *c;
+
+  bochs_printf("Prog3: Hello world C !\n");
+  for ( ; i> 0 ; i--)
+    continue;
+  bochs_printf("Prog3: Hello world D !\n");
+
+  return 0;
+}
diff -ruN /tmp/sos-code-article6.75/userland/myprog4.c ../sos-code-article7/userland/myprog4.c
--- /tmp/sos-code-article6.75/userland/myprog4.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog4.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,46 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog4.c
+ * Test unresolved page fault (write to unmapped page in user space)
+ */
+
+int main()
+{
+  char *c;
+  int i;
+  bochs_printf("Prog4: Hello world A !\n");
+  for (i = 0 ; i < 100000 ; i++)
+    continue;
+  bochs_printf("Prog4: Hello world B !\n");
+
+  /* Force a page fault */
+  c = (char*) 0x60000000;
+  /* Try to read somewhere in user space where nothing is mapped */
+  i = *c;
+
+  bochs_printf("Prog4: Hello world C !\n");
+  for ( ; i> 0 ; i--)
+    continue;
+  bochs_printf("Prog4: Hello world D !\n");
+
+  return 0;
+}
diff -ruN /tmp/sos-code-article6.75/userland/myprog5.c ../sos-code-article7/userland/myprog5.c
--- /tmp/sos-code-article6.75/userland/myprog5.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog5.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,36 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog5.c
+ * Test inifite loop (preemptive scheduling of threads in user mode)
+ */
+
+int main()
+{
+  int retval;
+
+  bochs_printf("Prog5: Hello world 1 !\n");
+  for (;;)
+    ;
+  bochs_printf("Prog5: Hello world 2 !\n");
+
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/userland/myprog6.c ../sos-code-article7/userland/myprog6.c
--- /tmp/sos-code-article6.75/userland/myprog6.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/myprog6.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,39 @@
+/* Copyright (C) 2005 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 <debug.h>
+
+/**
+ * @file myprog6.c
+ * Test execution of (forbiden) supervisor instructions
+ */
+
+int main()
+{
+  int i;
+  int retval = 0;
+
+  bochs_printf("Prog6: Hello world 1 !\n");
+  for (i = 0 ; i < 10000000 ; i++)
+    ;
+  bochs_printf("Prog6: Hello world 2 !\n");
+  asm("cli\n"); /* Protection volation ! */
+  bochs_printf("Prog6: Hello world 3 !\n");
+
+  return retval;
+}
diff -ruN /tmp/sos-code-article6.75/userland/stdarg.c ../sos-code-article7/userland/stdarg.c
--- /tmp/sos-code-article6.75/userland/stdarg.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/stdarg.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,244 @@
+/* Copyright (C) 2004  David Decotigny (with INSA Rennes for vsnprintf)
+   Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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 <string.h>
+
+#include "stdarg.h"
+
+
+/* I (d2) borrowed and rewrote this for Nachos/INSA Rennes. Thanks to
+   them for having kindly allowed me to do so. */
+int vsnprintf(char *buff, size_t len, const char * format, va_list ap)
+{
+  int i, result;
+  int fmt_modifiers = 0;
+  int prefix_long   = 0;
+  int prefix_long_long = 0;
+ 
+  if (!buff || !format || (len < 0))
+    return -1;
+  
+#define PUTCHAR(thechar) \
+  do { \
+    if (result < len-1) \
+      *buff++ = (thechar);  \
+    result++; \
+  } while (0)
+  
+  result = 0;
+  for(i=0 ; format[i] != '\0' ; i++)
+    {
+      if (!fmt_modifiers && (format[i] != '%'))
+	{
+	  PUTCHAR(format[i]);
+	  continue;
+	}
+	
+      switch (format[i])
+	{
+	case '%':
+	  if (fmt_modifiers)
+	    {
+	      PUTCHAR('%');
+	      fmt_modifiers = 0;
+	      break;
+	    }
+	  
+	  fmt_modifiers    = 1;
+	  prefix_long      = 0;
+	  prefix_long_long = 0;
+	  break;
+	  
+	case 'l':
+	  if (prefix_long)
+	    prefix_long_long = 1;
+	  else
+	    prefix_long = 1;
+	  break;
+	  
+	case 'u':
+	  {
+	    if (! prefix_long_long)
+	      {
+		unsigned int integer = va_arg(ap,unsigned int);
+		int cpt2 = 0;
+		char buff_int[16];
+		
+		do {
+		  int m10 = integer%10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	    else
+	      {
+		unsigned long long int integer
+		  = va_arg(ap,unsigned long long int);
+		int cpt2 = 0;
+		char buff_int[32];
+		
+		do {
+		  int m10 = integer%10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	  }
+	  fmt_modifiers = 0;
+	  break;
+
+	case 'i':
+	case 'd':
+	  {
+	    if (! prefix_long_long)
+	      {
+		int integer = va_arg(ap,int);
+		int cpt2 = 0;
+		char buff_int[16];
+		
+		if (integer<0)
+		  PUTCHAR('-');
+		/* Ne fait pas integer = -integer ici parce que INT_MIN
+		   n'a pas d'equivalent positif (int = [-2^31, 2^31-1]) */
+		
+		do {
+		  int m10 = integer%10;
+		  m10 = (m10 < 0)? -m10:m10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	    else
+	      {
+		long long int integer = va_arg(ap,long long int);
+		int cpt2 = 0;
+		char buff_int[32];
+		
+		if (integer<0)
+		  PUTCHAR('-');
+		/* Ne fait pas integer = -integer ici parce que INT_MIN
+		   n'a pas d'equivalent positif (int = [-2^63, 2^63-1]) */
+		
+		do {
+		  int m10 = integer%10;
+		  m10 = (m10 < 0)? -m10:m10;
+		  buff_int[cpt2++]=(char)('0'+ m10);
+		  integer=integer/10;
+		} while(integer!=0);
+	    
+		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
+		  PUTCHAR(buff_int[cpt2]);
+	      }
+	  }
+	  fmt_modifiers = 0;
+	  break;
+	  
+	case 'c':
+	  {
+	    int value = va_arg(ap,int);
+	    PUTCHAR((char)value);
+	    fmt_modifiers = 0;
+	    break;
+	  }
+	  
+	case 's':
+	  {
+	    char *string = va_arg(ap,char *);
+	    if (! string)
+	      string = "(null)";
+	    for( ; *string != '\0' ; string++)
+	      PUTCHAR(*string);
+	    fmt_modifiers = 0;
+	    break;
+	  }
+	  
+	case 'p':
+	  PUTCHAR('0');
+	  PUTCHAR('x');
+	case 'x':
+	  {
+	    unsigned long long int hexa;
+	    unsigned long long int nb;
+	    int i, had_nonzero = 0;
+	    
+	    if (prefix_long_long)
+	      hexa = va_arg(ap,unsigned long long int);
+	    else
+	      hexa = va_arg(ap,unsigned int);
+	    
+	    for(i=0 ; i < 16 ; i++)
+	      {
+		nb = (unsigned long long int)(hexa << (i*4));
+		nb = (nb >> 60) & 0xf;
+		// Skip the leading zeros
+		if (nb == 0)
+		  {
+		    if (had_nonzero)
+		      PUTCHAR('0');
+		  }
+		else
+		  {
+		    had_nonzero = 1;
+		    if (nb < 10)
+		      PUTCHAR('0'+nb);
+		    else
+		      PUTCHAR('a'+(nb-10));
+		  }
+	      }
+	    if (! had_nonzero)
+	      PUTCHAR('0');
+	  }
+	  fmt_modifiers = 0;
+	  break;
+	  
+	default:
+	  PUTCHAR('%');
+	  if (prefix_long)
+	    PUTCHAR('l');
+	  if (prefix_long_long)
+	    PUTCHAR('l');
+	  PUTCHAR(format[i]);
+	  fmt_modifiers = 0;
+	}
+    }
+
+  *buff = '\0';
+  return result;
+}
+
+
+int snprintf(char * buff, size_t len, const char *format, ...)
+{
+  va_list ap;
+ 
+  va_start(ap, format);
+  len = vsnprintf(buff, len, format, ap);
+  va_end(ap);
+ 
+  return len;
+}
diff -ruN /tmp/sos-code-article6.75/userland/stdarg.h ../sos-code-article7/userland/stdarg.h
--- /tmp/sos-code-article6.75/userland/stdarg.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/stdarg.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,50 @@
+/* Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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_LIBC_STDARG_H_
+#define _SOS_LIBC_STDARG_H_
+
+#include <types.h>
+
+/**
+ * @file stdarg.h
+ */
+
+/* Borrowed from GCC */
+#define __GNUC_VA_LIST 
+typedef void *__gnuc_va_list;
+typedef __gnuc_va_list va_list;
+#define __va_rounded_size(TYPE) \
+  (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
+#define va_start(AP, LASTARG) \
+  (AP = ((__gnuc_va_list) __builtin_next_arg (LASTARG)))
+#define va_end(AP) \
+  ((void)0)
+#define va_arg(AP, TYPE) \
+  (AP = (__gnuc_va_list) ((char *) (AP) + __va_rounded_size (TYPE)),  \
+   *((TYPE *) (void *) ((char *) (AP) - __va_rounded_size (TYPE))))
+#define __va_copy(dest, src) \
+  (dest) = (src)
+
+/* stdarg.h functions. There might be a non-standard behavior: there
+   will always be a trailing '\0' in the resulting string */
+int vsnprintf(char *, size_t, const char *, va_list);
+int snprintf(char *, size_t, const char *, /*args*/ ...)
+  __attribute__ ((format (printf, 3, 4)));
+
+#endif /* _SOS_LIBC_STDARG_H_ */
diff -ruN /tmp/sos-code-article6.75/userland/string.c ../sos-code-article7/userland/string.c
--- /tmp/sos-code-article6.75/userland/string.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/string.c	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,142 @@
+/* Copyright (C) 2004  David Decotigny (with INSA Rennes for vsnprintf)
+   Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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 "string.h"
+
+/* For an optimized version, see BSD sources ;) */
+void *memcpy(void *dst0, const void *src0, register unsigned int size)
+{
+  char *dst;
+  const char *src;
+  for (dst = (char*)dst0, src = (const char*)src0 ;
+       size > 0 ;
+       dst++, src++, size--)
+    *dst = *src;
+  return dst0;
+}
+
+/* ditto */
+void *memset(void *dst0, register int c, register unsigned int length)
+{
+  char *dst;
+  for (dst = (char*) dst0 ;
+       length > 0 ;
+       dst++, length --)
+    *dst = (char)c;
+  return dst0;
+}
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+  const unsigned char *c1, *c2;
+  unsigned int i;
+ 
+  for (i = 0, c1 = s1, c2 = s2; i < len; i++, c1++, c2++)
+    {
+      if(*c1 != *c2)
+        return *c1 - *c2;
+    }
+ 
+  return 0;
+}
+
+
+unsigned int strlen(register const char *str)
+{
+  unsigned int retval = 0;
+  
+  while (*str++)
+    retval++;
+  
+  return retval;
+}
+
+
+unsigned int strnlen(const char * s, size_t count)
+{
+  const char *sc;
+  
+  for (sc = s; count-- && *sc != '\0'; ++sc)
+    /* nothing */continue;
+
+  return sc - s;
+}
+
+
+char *strzcpy(register char *dst, register const char *src, register int len)
+{
+  int i;
+
+  if (len <= 0)
+    return dst;
+  
+  for (i = 0; i < len; i++)
+    {
+      dst[i] = src[i];
+      if(src[i] == '\0')
+        return dst;
+    }
+  
+  dst[len-1] = '\0'; 
+  return dst;
+}
+
+
+char *strzcat (char *dest, const char *src, size_t n)
+{
+  char *res = dest;
+  
+  for ( ; *dest ; dest++);
+  
+  for ( ; *src ; src++, dest++) {
+    *dest = *src;
+    n--;
+    if (n <= 0)
+      break;
+  }
+
+  *dest = '\0';
+  return res;
+}
+
+int strcmp(register const char *s1, register const char *s2)
+{
+  while (*s1 == *s2++)
+    if (*s1++ == 0)
+      return (0);
+  
+  return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
+}
+
+
+int strncmp(register const char *s1, register const char *s2, register int len)
+{
+  char c1 = '\0', c2 = '\0';
+  
+  while (len > 0)
+    {
+      c1 = (unsigned char) *s1++;
+      c2 = (unsigned char) *s2++;
+      if (c1 == '\0' || c1 != c2)
+        return c1 - c2;
+      len--;
+    }
+  
+  return c1 - c2;
+}
diff -ruN /tmp/sos-code-article6.75/userland/string.h ../sos-code-article7/userland/string.h
--- /tmp/sos-code-article6.75/userland/string.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/string.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,61 @@
+/* Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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_LIBC_STRING_H_
+#define _SOS_LIBC_STRING_H_
+
+#include <types.h>
+
+/**
+ * @file string.h
+ *
+ * Most of the prototypes of these functions are borrowed from
+ * FreeBSD, but their implementation (in klibc.c) come either from Kos
+ * (GPL v2) or from David Decotigny (SOS).
+ */
+
+void *memcpy(void *dst, const void *src, register unsigned int size ) ;
+void *memset(void *dst, register int c, register unsigned int length ) ;
+int memcmp(const void *s1, const void *s2, size_t n);
+
+unsigned int strlen( register const char *str) ;
+unsigned int strnlen(const char * s, size_t maxlen);
+
+/**
+ * @note Same as strncpy(), with a slightly different semantic.
+ * Actually, strncpy(3C) says " The result will not be null-terminated
+ * if the length of 'from' is n or more.".  Here, 'dst' is ALWAYS
+ * null-terminated. And its total len will ALWAYS be <= len, with
+ * null-terminating-char included.
+ */
+char *strzcpy( register char *dst, register const char *src,
+               register int len ) ;
+
+/**
+ * @note Same as strncat(), with the same semantic : 'dst' is ALWAYS
+ * null-terminated. And its total len will ALWAYS be <= len, with
+ * null-terminating-char included.
+ */
+char *strzcat (char *dest, const char *src,
+               const size_t len);
+ 
+int strcmp(register const char *s1, register const char *s2 );
+int strncmp(register const char *s1, register const char *s2,
+	    register int len );
+
+#endif /* _SOS_LIBC_STRING_H_ */
diff -ruN /tmp/sos-code-article6.75/userland/types.h ../sos-code-article7/userland/types.h
--- /tmp/sos-code-article6.75/userland/types.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article7/userland/types.h	2005-04-27 20:14:23.000000000 +0200
@@ -0,0 +1,29 @@
+/* Copyright (C) 2003  The KOS Team
+   Copyright (C) 1999  Free Software Foundation
+
+   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_LIBC_TYPES_H_
+#define _SOS_LIBC_TYPES_H_
+
+/**
+ * @file types.h
+ */
+
+typedef unsigned int size_t;
+
+
+#endif /* _SOS_LIBC_TYPES_H_ */
diff -ruN /tmp/sos-code-article6.75/VERSION ../sos-code-article7/VERSION
--- /tmp/sos-code-article6.75/VERSION	2005-01-04 04:13:51.000000000 +0100
+++ ../sos-code-article7/VERSION	2005-04-27 20:14:16.000000000 +0200
@@ -1,9 +1,7 @@
 SOS -- Simple OS
 Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni)
 
-Version "Article 6 (2nd part)" -- Basic kernel thread and synchronization API
-                                  with priority ordering and O(1)-style
-                                  scheduler (Linux)
+Version "Article 7 (1st part)" -- Basic user programs support
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
