/tmp/sos-code-article6/Makefile (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/Makefile (2005-03-02 17:22:07.000000000 +0100 )
Line 1 
Line 1 
  ## 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 CC=gcc
 CFLAGS  = -Wall -nostdlib -nostdinc -ffreestanding -DKERNEL_SOS LD=ld
 LDFLAGS = --warn-common CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS
  LDFLAGS = --warn-common -nostdlib
           hwcore/idt.o hwcore/gdt.o                             \           hwcore/idt.o hwcore/gdt.o                             \
           hwcore/exception.o hwcore/exception_wrappers.o        \           hwcore/exception.o hwcore/exception_wrappers.o        \
Line 10 
Line 28 
           hwcore/cpu_context.o hwcore/cpu_context_switch.o        \           hwcore/cpu_context.o hwcore/cpu_context_switch.o        \
           sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o                \           sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o                \
           sos/physmem.o sos/klibc.o                                \           sos/physmem.o sos/klibc.o                                \
           sos/assert.o sos/main.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
 KERNEL_OBJ   = sos.elf KERNEL_OBJ   = sos.elf
 MULTIBOOT_IMAGE = fd.img MULTIBOOT_IMAGE = fd.img
Line 37 
Line 57 
 %.o: %.S %.o: %.S
         $(CC) -I$(PWD) -c $< $(CFLAGS) -DASM_SOURCE=1 -o $@         $(CC) -I$(PWD) -c $< $(CFLAGS) -DASM_SOURCE=1 -o $@
  
  FORCE:
          @
  
 # Clean directory # Clean directory
 clean: clean:
         $(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.map         $(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.map
  
 

/tmp/sos-code-article6/VERSION (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/VERSION (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 SOS -- Simple OS SOS -- Simple OS
 Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni) Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni)
  
 Version "Article 6 (1st part)" -- Low-level kernel thread management Version "Article 6 (2nd part [FIFO])" -- Basic kernel thread and
                                           synchronization API with basic
                                           FIFO scheduler
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/drivers/bochs.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/drivers/bochs.c (2005-03-02 17:22:07.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 30 
Line 29 
   return SOS_OK;   return SOS_OK;
 } }
  
  #define _putchar(chr) \
 #define sos_bochs_putchar(chr) \ 
  
  sos_ret_t sos_bochs_putchar(char c)
  {
    _putchar(c);
  
    return SOS_OK;
  }
  
 sos_ret_t sos_bochs_putstring(const char* str) sos_ret_t sos_bochs_putstring(const char* str)
 { {
   for ( ; str && (*str != '\0') ; str++)   for ( ; str && (*str != '\0') ; str++)
     sos_bochs_putchar(*str);     _putchar(*str);
   return SOS_OK;   return SOS_OK;
 } }
  
  
 { {
   unsigned c;   unsigned c;
  
 #define BOCHS_PRTHEX(q) \ #define BOCHS_PRTHEX(q) \
   ({ unsigned char r; if ((q) >= 10) r='a'+(q)-10; \   ({ unsigned char r; if ((q) >= 10) r='a'+(q)-10; \
      else r='0'+(q); sos_bochs_putchar(r); })      else r='0'+(q); _putchar(r); })
   switch (nbytes)   switch (nbytes)
     {     {
  
 

/tmp/sos-code-article6/drivers/bochs.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/drivers/bochs.h (2005-03-02 17:22:07.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 34 
Line 33 
  
 sos_ret_t sos_bochs_setup(void); sos_ret_t sos_bochs_setup(void);
  
  sos_ret_t sos_bochs_putchar(char c);
  
 sos_ret_t sos_bochs_putstring(const char* str); sos_ret_t sos_bochs_putstring(const char* str);
  
 /** Print the least signficant 32 (nbytes == 4), 24 (nbytes == 3), 16 /** Print the least signficant 32 (nbytes == 4), 24 (nbytes == 3), 16
  
 

/tmp/sos-code-article6/drivers/x86_videomem.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/drivers/x86_videomem.c (2005-03-02 17:22:07.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/drivers/x86_videomem.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/drivers/x86_videomem.h (2005-03-02 17:22:07.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/hwcore/cpu_context.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/cpu_context.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2000-2004, The KOS team /* Copyright (C) 2005  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc.    Copyright (C) 2000-2004, The KOS team
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 40 
Line 40 
  * the registers are stored on the stack in  * the registers are stored on the stack in
  * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.  * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.
  */  */
 struct sos_cpu_kstate { struct sos_cpu_state {
  
   /* These are SOS convention */   /* These are SOS convention */
Line 48 
Line 48 
   sos_ui16_t  fs;   sos_ui16_t  fs;
   sos_ui16_t  es;   sos_ui16_t  es;
   sos_ui16_t  ds;   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_ui32_t  eax;   sos_ui32_t  eax;
   sos_ui32_t  ebx;   sos_ui32_t  ebx;
Line 61 
Line 63 
   /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */   /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
   sos_ui32_t  error_code;   sos_ui32_t  error_code;
   sos_vaddr_t eip;   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 */
  
   /* (Higher addresses) */   /* (Higher addresses) */
 } __attribute__((packed)); } __attribute__((packed));
  
  
  /**
   * The CS value pushed on the stack by the CPU upon interrupt, and
   * needed by the iret instruction, is 32bits long while the real CPU
   * CS register is 16bits only: this macro simply retrieves the CPU
   * "CS" register value from the CS value pushed on the stack by the
   * CPU upon interrupt.
   *
   * The remaining 16bits pushed by the CPU should be considered
   * "reserved" and architecture dependent. IMHO, the specs don't say
   * anything about them. Considering that some architectures generate
   * non-zero values for these 16bits (at least Cyrix), we'd better
   * ignore them.
   */
  #define GET_CPU_CS_REGISTER_VALUE(pushed_ui32_cs_value) \
    ( (pushed_ui32_cs_value) & 0xffff )
  
  
  /**
   * Structure of an interrupted Kernel thread's context
   */
  struct sos_cpu_kstate
  {
    struct sos_cpu_state regs;
  } __attribute__((packed));
  
  
  /**
   * THE main operation of a kernel thread. This routine calls the
   * kernel thread function start_func and calls exit_func when
   * start_func returns.
   */
 static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, static void core_routine (sos_cpu_kstate_function_arg1_t *start_func,
                           sos_ui32_t start_arg,                           sos_ui32_t start_arg,
                           sos_cpu_kstate_function_arg1_t *exit_func,                           sos_cpu_kstate_function_arg1_t *exit_func,
Line 88 
Line 121 
 } }
  
  
 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_ui32_t  start_arg,                               sos_ui32_t  start_arg,
                               sos_vaddr_t stack_bottom,                               sos_vaddr_t stack_bottom,
Line 96 
Line 129 
                               sos_cpu_kstate_function_arg1_t *exit_func,                               sos_cpu_kstate_function_arg1_t *exit_func,
                               sos_ui32_t  exit_arg)                               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   /* This is a critical internal function, so that it is assumed that
      the caller knows what he does: we legitimally assume that values      the caller knows what he does: we legitimally assume that values
      for ctxt, start_func, stack_* and exit_func are allways VALID ! */      for ctxt, start_func, stack_* and exit_func are allways VALID ! */
Line 122 
Line 158 
   sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr;   sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr;
  
   /* If needed, poison the stack */   /* If needed, poison the stack */
 #ifdef SOS_CPU_KSTATE_DETECT_UNINIT_VARS #ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS
   memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, stack_size);   memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, stack_size);
 #elif defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) #elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
   sos_cpu_kstate_prepare_detect_stack_overflow(stack_bottom, stack_size);   sos_cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size);
  
   /* Simulate a call to the core_routine() function: prepare its   /* Simulate a call to the core_routine() function: prepare its
Line 145 
Line 181 
   /* Compute the base address of the structure, which must be located   /* Compute the base address of the structure, which must be located
      below the previous elements */      below the previous elements */
   tmp_vaddr  = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate);   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 */   /* 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   /* Tell the CPU context structure that the first instruction to
      execute will be that of the core_routine() function */      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 */   /* Setup the segment registers */
   (*ctxt)->cs  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KCODE); /* Code */   kctxt->regs.cs
   (*ctxt)->ds  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE); /* Code */
   (*ctxt)->es  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */   kctxt->regs.ds
   (*ctxt)->ss  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Stack */     = 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 */
  
   /* The newly created context is initially interruptible */   /* 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;   return SOS_OK;
 } }
  
  
 #if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) #if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
 sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
                                              sos_vaddr_t stack_bottom,                                                    sos_vaddr_t stack_bottom,
                                              sos_size_t stack_size)                                                    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;
     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 void
 sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
                                      sos_vaddr_t stack_bottom,                                            sos_vaddr_t stack_bottom,
                                      sos_size_t stack_size)                                            sos_size_t stack_size)
   unsigned char *c;   unsigned char *c;
   int i;   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) >= stack_bottom);
   SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate)   SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate)
                    <= stack_bottom + stack_size);                    <= stack_bottom + stack_size);
  
    /* Check that the bottom of the stack has not been altered */
   for (c = (unsigned char*) stack_bottom, i = 0 ;   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) ;
     {     {
       SOS_ASSERT_FATAL(SOS_CPU_KSTATE_STACK_POISON == *c);       SOS_ASSERT_FATAL(SOS_CPU_STATE_STACK_POISON == *c);
 } }
 #endif #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);   SOS_ASSERT_FATAL(NULL != ctxt);
  
    /* This is the PC of the interrupted context (ie kernel or user
       context). */
   return ctxt->eip;   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);   SOS_ASSERT_FATAL(NULL != ctxt);
  
    /* On SOS, "ctxt" corresponds to the address of the esp register of
       the saved context in Kernel mode (always, even for the interrupted
       context of a user thread). */
   return (sos_vaddr_t)ctxt;   return (sos_vaddr_t)ctxt;
 } }
  
  
 void sos_cpu_kstate_dump(const struct sos_cpu_kstate *ctxt) void sos_cpu_context_dump(const struct sos_cpu_state *ctxt)
   char buf[128];   char buf[128];
   snprintf(buf, sizeof(buf),   snprintf(buf, sizeof(buf),
            "CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x",            "CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x",
            (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags,            (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,
   sos_bochs_putstring(buf); sos_bochs_putstring("\n");   sos_bochs_putstring(buf); sos_bochs_putstring("\n");
   sos_x86_videomem_putstring(23, 0,   sos_x86_videomem_putstring(23, 0,
Line 232 
Line 294 
 } }
  
  
 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);   SOS_ASSERT_FATAL(NULL != ctxt);
   return ctxt->error_code;   return ctxt->error_code;
Line 240 
Line 307 
  
  
 sos_vaddr_t 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;   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).
     */
                 :"=r"(cr2)                 :"=r"(cr2)
                 : );                 : );
Line 254 
Line 332 
 } }
  
  
 sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate, /* =======================================================================
   * Backtrace facility. To be used for DEBUGging purpose ONLY.
   */
  
  
  sos_ui32_t sos_backtrace(const struct sos_cpu_state *cpu_state,
                          sos_vaddr_t stack_bottom,                          sos_vaddr_t stack_bottom,
                          sos_size_t stack_size,                          sos_size_t stack_size,
Line 293 
Line 376 
    * function will return -SOS_ENOSUP.    * function will return -SOS_ENOSUP.
    */    */
  
   if (cpu_kstate)   if (cpu_state)
       callee_PC    = cpu_kstate->eip;       callee_PC    = cpu_state->eip;
       caller_frame = cpu_kstate->ebp;       caller_frame = cpu_state->ebp;
   else   else
     {     {
  
 

/tmp/sos-code-article6/hwcore/cpu_context.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/cpu_context.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2000-2004, The KOS team /* Copyright (C) 2005  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc.    Copyright (C) 2000-2004, The KOS team
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 32 
Line 32 
  
  
 /** /**
  * Opaque structure storing the CPU context of an inactive kernel  * Opaque structure storing the CPU context of an inactive kernel or
  * thread, as saved by the low level primitives below or by the  * user thread, as saved by the low level primitives below or by the
  *  *
  * @note This is an (architecture-independent) forward declaration:  * @note This is an (architecture-independent) forward declaration:
  * see cpu_context.c and the *.S files for its  * see cpu_context.c and the *.S files for its
  * (architecture-dependent) definition.  * (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)); typedef void (sos_cpu_kstate_function_arg1_t(sos_ui32_t arg1));
  
Line 56 
Line 57 
  * start_func function returns, the function exit_func is called with  * start_func function returns, the function exit_func is called with
  * argument exit_arg.  * argument exit_arg.
  *  *
  * @param ctxt The kernel thread CPU context to initialize. The  * @param kctxt The kernel thread CPU context to initialize. The
  * address of the newly-initialized struct sos_cpu_kstate will be  * address of the newly-initialized struct sos_cpu_state will be
  * stored in this variable. The contents of this struct sos_cpu_kstate  * stored in this variable. The contents of this struct sos_cpu_state
  *  *
  * @param start_func The address of the first instruction that will be  * @param start_func The address of the first instruction that will be
Line 83 
Line 84 
  *  *
  * @note the newly created context is INTERRUPTIBLE by default !  * @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_ui32_t  start_arg,                               sos_ui32_t  start_arg,
                               sos_vaddr_t stack_bottom,                               sos_vaddr_t stack_bottom,
Line 93 
Line 94 
  
  
 /** /**
  * Function that performs an immediate context-switch from one kernel  * Function that performs an immediate context-switch from one
  * thread to another one. It stores the current executing context in  * kernel/user thread to another one. It stores the current executing
  * from_ctxt, and restores to_context on CPU.  * 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
  *  *
  * @param to_ctxt The CPU will resume its execution with the struct  * @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, void sos_cpu_context_switch(struct sos_cpu_state **from_ctxt,
                            struct sos_cpu_kstate *to_ctxt);                             struct sos_cpu_state *to_ctxt);
  
 /* /*
  * Switch to the new given context (of a kernel thread) without saving  * Switch to the new given context (of a kernel/user thread) without
  * the old context (of another kernel thread), and call the function  * saving the old context (of another kernel/user thread), and call
  * reclaiming_func passing it the recalining_arg argument. The  * the function reclaiming_func passing it the recalining_arg
  * reclaining function is called from within the stack of the new  * argument. The reclaining function is called from within the stack
  * context, so that it can (among other things) safely destroy the  * of the new context, so that it can (among other things) safely
  * stack of the former context.  * destroy the stack of the former context.
  * @param switch_to_ctxt The context that will be restored on the CPU  * @param switch_to_ctxt The context that will be restored on the CPU
  *  *
Line 122 
Line 123 
  * context to switch_to_ctxt.  * context to switch_to_ctxt.
  */  */
 void void
 sos_cpu_kstate_exit_to(struct sos_cpu_kstate *switch_to_ctxt, sos_cpu_context_exit_to(struct sos_cpu_state *switch_to_ctxt,
                        sos_cpu_kstate_function_arg1_t *reclaiming_func,                         sos_cpu_kstate_function_arg1_t *reclaiming_func,
                        sos_ui32_t reclaiming_arg) __attribute__((noreturn));                         sos_ui32_t reclaiming_arg) __attribute__((noreturn));
  
 /* ======================================================================= /* =======================================================================
  * Public Accessor functions  * Public Accessor functions
  */  */
  
  
 /** /**
  * 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)  * 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);
  
 /* ======================================================================= /* =======================================================================
Line 158 
Line 159 
  * Return the argument passed by the CPU upon exception, as stored in the  * Return the argument passed by the CPU upon exception, as stored in the
  * saved context  * 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  * Return the faulting address of the exception
  */  */
 sos_vaddr_t 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);
  
 /* ======================================================================= /* =======================================================================
Line 175 
Line 176 
  *  - when the thread might have gone too deep in the stack  *  - when the thread might have gone too deep in the stack
  */  */
 /** The signature of the poison */ /** 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  * When set, mean that the whole stack is poisoned to detect use of
  * unititialized variables  * unititialized variables
  */  */
 #define SOS_CPU_KSTATE_DETECT_UNINIT_VARS #define SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS
 /* #undef SOS_CPU_KSTATE_DETECT_UNINIT_VARS */ /* #undef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS */
 /** /**
  * When set, mean that the bottom of the stack is poisoned to detect  * When set, mean that the bottom of the stack is poisoned to detect
  * probable stack overflow. Its value indicates the number of bytes  * probable stack overflow. Its value indicates the number of bytes
  * used for this detection.  * used for this detection.
  */  */
 #define SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW  64 #define SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW  64
 /* #undef SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW */ /* #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)
 sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
                                              sos_vaddr_t stack_bottom,                                                    sos_vaddr_t kernel_stack_bottom,
                                              sos_size_t stack_size);                                                    sos_size_t kernel_stack_size);
 void sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, void sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
                                           sos_vaddr_t stack_bottom,                                                 sos_vaddr_t kernel_stack_bottom,
                                           sos_size_t stack_size);                                                 sos_size_t kernel_stack_size);
 # define sos_cpu_kstate_prepare_detect_stack_overflow(ctxt,stkbottom,stksize) \ # define sos_cpu_state_prepare_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \
 # define sos_cpu_kstate_detect_stack_overflow(ctxt,stkbottom,stksize) \ # define sos_cpu_state_detect_kernel_stack_overflow(ctxt,stkbottom,stksize) \
 #endif #endif
  
Line 235 
Line 236 
  
  
 /** /**
  * 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  * @param cpu_state The CPU context we want to explore. MUST be the
  * backtrace the current CPU context.  * context of a thread in Kernel mode, or NULL. When NULL: backtrace
   * the current CPU context.
  * @param max_depth The maximum number of frames to explore  * @param max_depth The maximum number of frames to explore
  *  *
Line 258 
Line 260 
  * @note Might be inaccurate when gcc's -fomit-frame-pointer has been  * @note Might be inaccurate when gcc's -fomit-frame-pointer has been
  * used.  * 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_vaddr_t stack_bottom,                          sos_vaddr_t stack_bottom,
                          sos_size_t stack_size,                          sos_size_t stack_size,
  
 

/tmp/sos-code-article6/hwcore/cpu_context_switch.S (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/cpu_context_switch.S (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2000-2004, The KOS team /* Copyright (C) 2005  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc.    Copyright (C) 2000-2004, The KOS team
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 17 
Line 17 
    USA.     USA. 
 */ */
 #define ASM_SOURCE 1 #define ASM_SOURCE 1
  
                    
 .file "cpu_context_switch.S" .file "cpu_context_switch.S"
  
 .text .text
  
                  
 .globl sos_cpu_kstate_switch .globl sos_cpu_context_switch
 .type sos_cpu_kstate_switch, @function .type sos_cpu_context_switch, @function
 sos_cpu_kstate_switch: sos_cpu_context_switch:
         // arg1= from_context  --    esp+60         // arg1= from_context  --    esp+60
         // caller ip           --    esp+56         // caller ip           --    esp+56
Line 48 
Line 49 
   pushw %gs          //              esp   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 */   /* Store the address of the saved context */
Line 78 
Line 79 
   iret /* equivalent to: popfl ; ret */   iret /* equivalent to: popfl ; ret */
  
 resume_pc: resume_pc:
         // Same context as that when sos_cpu_kstate_switch got called         // Same context as that when sos_cpu_context_switch got called
         // arg1= from_context  -- esp+4         // arg1= from_context  -- esp+4
         // caller ip           -- esp         // caller ip           -- esp
Line 87 
Line 88 
  
  
 /* ------------------------- */ /* ------------------------- */
 .globl sos_cpu_kstate_exit_to .globl sos_cpu_context_exit_to
 .type sos_cpu_kstate_exit_to, @function .type sos_cpu_context_exit_to, @function
 sos_cpu_kstate_exit_to: sos_cpu_context_exit_to:
         // arg2= reclaiming_func -- esp+8         // arg2= reclaiming_func -- esp+8
         // arg1= to_context      -- esp+4         // arg1= to_context      -- esp+4
Line 125 
Line 126 
  
   /* This restores the eflags, the cs and the eip registers */   /* This restores the eflags, the cs and the eip registers */
   iret /* equivalent to: popfl ; ret */   iret /* equivalent to: popfl ; ret */
  
 

/tmp/sos-code-article6/hwcore/exception.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/exception.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 19 
Line 18 
 #include "idt.h" #include "idt.h"
 #include "irq.h" #include "irq.h"
  
  #include <sos/assert.h>
 #include "exception.h" #include "exception.h"
  
 /* array of exception wrappers, defined in exception_wrappers.S */ /* array of exception wrappers, defined in exception_wrappers.S */
Line 28 
Line 28 
 sos_exception_handler_t sos_exception_handler_array[SOS_EXCEPT_NUM] = sos_exception_handler_t sos_exception_handler_array[SOS_EXCEPT_NUM] =
   { NULL, };   { NULL, };
  
 sos_ret_t sos_exceptions_setup(void) /* List of exception names for the x86 architecture */
  static const char * sos_x86_exnames[] = {
    [SOS_EXCEPT_DIVIDE_ERROR]                = "Division by zero",
    [SOS_EXCEPT_DEBUG]                       = "Debug",
    [SOS_EXCEPT_NMI_INTERRUPT]               = "Non Maskable Interrupt",
    [SOS_EXCEPT_BREAKPOINT]                  = "Breakpoint",
    [SOS_EXCEPT_OVERFLOW]                    = "Overflow",
    [SOS_EXCEPT_BOUND_RANGE_EXCEDEED]        = "Bound Range Exceeded",
    [SOS_EXCEPT_INVALID_OPCODE]              = "Invalid Opcode",
    [SOS_EXCEPT_DEVICE_NOT_AVAILABLE]        = "Device Unavailable",
    [SOS_EXCEPT_DOUBLE_FAULT]                = "Double Fault",
    [SOS_EXCEPT_COPROCESSOR_SEGMENT_OVERRUN] = "Coprocessor Segment Overrun",
    [SOS_EXCEPT_INVALID_TSS]                 = "Invalid TSS",
    [SOS_EXCEPT_SEGMENT_NOT_PRESENT]         = "Segment Not Present",
    [SOS_EXCEPT_STACK_SEGMENT_FAULT]         = "Stack Segfault",
    [SOS_EXCEPT_GENERAL_PROTECTION]          = "General Protection",
    [SOS_EXCEPT_PAGE_FAULT]                  = "Page Fault",
    [SOS_EXCEPT_INTEL_RESERVED_1]            = "INTEL1",
    [SOS_EXCEPT_FLOATING_POINT_ERROR]        = "FP Error",
    [SOS_EXCEPT_ALIGNEMENT_CHECK]            = "Alignment Check",
    [SOS_EXCEPT_MACHINE_CHECK]               = "Machine Check",
    [SOS_EXCEPT_INTEL_RESERVED_2]            = "INTEL2",
    [SOS_EXCEPT_INTEL_RESERVED_3]            = "INTEL3",
    [SOS_EXCEPT_INTEL_RESERVED_4]            = "INTEL4",
    [SOS_EXCEPT_INTEL_RESERVED_5]            = "INTEL5",
    [SOS_EXCEPT_INTEL_RESERVED_6]            = "INTEL6",
    [SOS_EXCEPT_INTEL_RESERVED_7]            = "INTEL7",
    [SOS_EXCEPT_INTEL_RESERVED_8]            = "INTEL8",
    [SOS_EXCEPT_INTEL_RESERVED_9]            = "INTEL9",
    [SOS_EXCEPT_INTEL_RESERVED_10]           = "INTEL10",
    [SOS_EXCEPT_INTEL_RESERVED_11]           = "INTEL11",
    [SOS_EXCEPT_INTEL_RESERVED_12]           = "INTEL12",
    [SOS_EXCEPT_INTEL_RESERVED_13]           = "INTEL13",
    [SOS_EXCEPT_INTEL_RESERVED_14]           = "INTEL14"
  };
  
  
  /* Catch-all exception handler */
  static void sos_generic_ex(int exid, const struct sos_cpu_state *ctxt)
    const char *exname = sos_exception_get_name(exid);
  
    sos_display_fatal_error("Exception %s in Kernel at instruction 0x%x (info=%x)!\n",
                            exname,
                            sos_cpu_context_get_PC(ctxt),
                            (unsigned)sos_cpu_context_get_EX_info(ctxt));
  }
  
  
  sos_ret_t sos_exception_subsystem_setup(void)
  {
    sos_ret_t retval;
    int exid;
  
    /* Setup the generic exception handler by default for everybody
       except for the double fault exception */
    for (exid = 0 ; exid < SOS_EXCEPT_NUM ; exid ++)
      {
        /* Skip double fault (see below) */
        if (exid == SOS_EXCEPT_DOUBLE_FAULT)
          continue;
  
        retval = sos_exception_set_routine(exid, sos_generic_ex);
        if (SOS_OK != retval)
          return retval;
      }
  
  
   /* We inidicate that the double fault exception handler is defined,   /* We inidicate that the double fault exception handler is defined,
      and give its address. this handler is a do-nothing handler (see      and give its address. this handler is a do-nothing handler (see
      exception_wrappers.S), and it can NOT be overriden by the      exception_wrappers.S), and it can NOT be overriden by the
Line 90 
Line 156 
   /* Expected to be atomic */   /* Expected to be atomic */
   return sos_exception_handler_array[exception_number];   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];
  }
  
 

/tmp/sos-code-article6/hwcore/exception.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/exception.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 71 
Line 70 
 #ifndef ASM_SOURCE #ifndef ASM_SOURCE
  
 typedef void (*sos_exception_handler_t)(int exception_number, typedef void (*sos_exception_handler_t)(int exception_number,
                                         const struct sos_cpu_kstate *cpu_kstate);                                         const struct sos_cpu_state *cpu_kstate);
 sos_ret_t sos_exceptions_setup(void); sos_ret_t sos_exception_subsystem_setup(void);
                                     sos_exception_handler_t routine);                                     sos_exception_handler_t routine);
 sos_exception_handler_t sos_exception_get_routine(int exception_number); sos_exception_handler_t sos_exception_get_routine(int exception_number);
  
  const char * sos_exception_get_name(int exception_number);
 #endif /* ! ASM_SOURCE */ #endif /* ! ASM_SOURCE */
  
 #endif /* _SOS_HWEXCEPT_H_ */ #endif /* _SOS_HWEXCEPT_H_ */
  
 

/tmp/sos-code-article6/hwcore/exception_wrappers.S (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/exception_wrappers.S (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The KOS Team /* Copyright (C) 2004  The KOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 29 
Line 28 
    with exception.c */    with exception.c */
 .globl sos_exception_wrapper_array .globl sos_exception_wrapper_array
  
          
  * For exceptions with/without error code, refer to Intel x86 doc vol 3,  * For exceptions with/without error code, refer to Intel x86 doc vol 3,
  * section 5.12  * section 5.12
  
 

/tmp/sos-code-article6/hwcore/gdt.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/gdt.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* 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    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 38 
Line 38 
   sos_ui8_t  segment_type:4;        /* Section 3.4.3.1 (code/data)   sos_ui8_t  segment_type:4;        /* Section 3.4.3.1 (code/data)
                                        and 3.5 (system) of Intel x86 vol 3 */                                        and 3.5 (system) of Intel x86 vol 3 */
   sos_ui8_t  descriptor_type:1;     /* 0=system, 1=Code/Data */   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  limit_19_16:4;         /* Segment limit, bits 19..16 */   sos_ui8_t  limit_19_16:4;         /* Segment limit, bits 19..16 */
Line 116 
Line 116 
   [SOS_SEG_KDATA] = BUILD_GDTE(0, 0),   [SOS_SEG_KDATA] = BUILD_GDTE(0, 0),
 }; };
  
 sos_ret_t sos_gdt_setup(void) sos_ret_t sos_gdt_subsystem_setup(void)
   struct x86_gdt_register gdtr;   struct x86_gdt_register gdtr;
  
Line 148 
Line 148 
  
   return SOS_OK;   return SOS_OK;
 } }
  
  
  
 

/tmp/sos-code-article6/hwcore/gdt.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/gdt.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 30 
Line 29 
  * @see Intel x86 doc vol 3, chapter 3  * @see Intel x86 doc vol 3, chapter 3
  */  */
  
  #include <sos/types.h>
 #include <sos/errno.h> #include <sos/errno.h>
  
 /** /**
  * Configure the virtual space as a direct mapping to the linear  * Configure the virtual space as a direct mapping to the linear
  * address space (ie "flat" virtual space).  * address space (ie "flat" virtual space).
  */  */
 sos_ret_t sos_gdt_setup(void); sos_ret_t sos_gdt_subsystem_setup(void);
  
 #endif /* _SOS_GDT_H_ */ #endif /* _SOS_GDT_H_ */
  
 

/tmp/sos-code-article6/hwcore/i8254.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/i8254.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The KOS Team /* Copyright (C) 2004  The KOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/hwcore/i8254.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/i8254.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/hwcore/i8259.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/i8259.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The KOS Team /* Copyright (C) 2004  The KOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 24 
Line 23 
 #define PIC_SLAVE  0xa0 #define PIC_SLAVE  0xa0
  
 /** Setup the 8259 PIC */ /** Setup the 8259 PIC */
 sos_ret_t sos_i8259_setup(void) sos_ret_t sos_i8259_subsystem_setup(void)
   /* Send ICW1: 8086 mode + NOT Single ctrl + call address   /* Send ICW1: 8086 mode + NOT Single ctrl + call address
      interval=8 */      interval=8 */
  
 

/tmp/sos-code-article6/hwcore/i8259.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/i8259.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 32 
Line 31 
  */  */
  
 /** Setup PIC and Disable all IRQ lines */ /** Setup PIC and Disable all IRQ lines */
 sos_ret_t sos_i8259_setup(void); sos_ret_t sos_i8259_subsystem_setup(void);
 sos_ret_t sos_i8259_enable_irq_line(int numirq); sos_ret_t sos_i8259_enable_irq_line(int numirq);
  
  
 

/tmp/sos-code-article6/hwcore/idt.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/idt.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 70 
Line 69 
  
 static struct x86_idt_entry    idt[SOS_IDTE_NUM]; static struct x86_idt_entry    idt[SOS_IDTE_NUM];
  
 sos_ret_t sos_idt_setup() sos_ret_t sos_idt_subsystem_setup()
   struct x86_idt_register idtr;   struct x86_idt_register idtr;
   int i;   int i;
  
 

/tmp/sos-code-article6/hwcore/idt.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/idt.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 57 
Line 56 
  
 /** Initialization routine: all the IDT entries (or "IDTE") are marked /** Initialization routine: all the IDT entries (or "IDTE") are marked
     "not present". */     "not present". */
 sos_ret_t sos_idt_setup(void); sos_ret_t sos_idt_subsystem_setup(void);
 /** /**
  * Enable the IDT entry if handler_address != NULL, with the given  * Enable the IDT entry if handler_address != NULL, with the given
  
 

/tmp/sos-code-article6/hwcore/ioports.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/ioports.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  All GPL'ed OS /* 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    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article6/hwcore/irq.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/irq.c (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 30 
Line 29 
 /** Number of interrupt handlers that are currently executing */ /** Number of interrupt handlers that are currently executing */
 sos_ui32_t sos_irq_nested_level_counter; sos_ui32_t sos_irq_nested_level_counter;
  
 sos_ret_t sos_irq_setup(void) sos_ret_t sos_irq_subsystem_setup(void)
   sos_irq_nested_level_counter = 0;   sos_irq_nested_level_counter = 0;
   return sos_i8259_setup();   return sos_i8259_subsystem_setup();
  
  
  
 

/tmp/sos-code-article6/hwcore/irq.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/irq.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 63 
Line 62 
  
  
 /** Definition of an hardware IRQ handler */ /** Definition of an hardware IRQ handler */
 typedef void (*sos_irq_handler_t)(int irq_level, typedef void (*sos_irq_handler_t)(int irq_level);
                                   const struct sos_cpu_kstate *cpu_kstate); 
  
 /** Setup the PIC */ /** Setup the PIC */
 sos_ret_t sos_irq_setup(void); sos_ret_t sos_irq_subsystem_setup(void);
  
 /** /**
Line 90 
Line 88 
  
  
 /** /**
  * Tell whether we are currently executing in interrupt context  * Return TRUE when we are currently executing in interrupt context
 #define sos_servicing_interrupt() \ #define sos_servicing_irq() \
  
  
  
 

/tmp/sos-code-article6/hwcore/irq_wrappers.S (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/irq_wrappers.S (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The KOS Team /* Copyright (C) 2004  The KOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 16 
Line 15 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    USA.     USA. 
 */ */
 #define ASM_SOURCE 1 
 .file "irq_wrappers.S" .file "irq_wrappers.S"
  
Line 75 
Line 73 
                 outb  %al, $0x20                 outb  %al, $0x20
                  
                 /*                 /*
                  * Call the handler with the IRQ number and the                  * Call the handler with IRQ number as argument
                  * address of the stored CPU context as arguments 
                 pushl %esp 
                 leal  sos_irq_handler_array,%edi                 leal  sos_irq_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 /* Unallocate the arguments passed to the handler */                 addl  $4, %esp
                 addl  $8, %esp 
                 /*                 /*
                  * Decrement IRQ nested level                  * Decrement IRQ nested level
Line 168 
Line 163 
                 outb  %al, $0x20                 outb  %al, $0x20
  
                 /*                 /*
                  * Call the handler with the IRQ number and the                  * Call the handler with IRQ number as argument
                  * address of the stored CPU context as arguments 
                 pushl %esp 
                 leal  sos_irq_handler_array,%edi                 leal  sos_irq_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 /* Unallocate the arguments passed to the handler */                 addl  $4, %esp
                 addl  $8, %esp 
                 /*                 /*
                  * Decrement IRQ nested level                  * Decrement IRQ nested level
  
 

/tmp/sos-code-article6/hwcore/paging.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/paging.c (2005-03-02 17:22:08.000000000 +0100 )
Line 21 
Line 21 
  
 #include "paging.h" #include "paging.h"
  
  
 /** The structure of a page directory entry. See Intel vol 3 section /** The structure of a page directory entry. See Intel vol 3 section
     3.6.4 */     3.6.4 */
 struct x86_pde struct x86_pde
Line 182 
Line 183 
 } }
  
  
 sos_ret_t sos_paging_setup(sos_paddr_t identity_mapping_base, sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base,
                            sos_paddr_t identity_mapping_top)                                      sos_paddr_t identity_mapping_top)
   /* The PDBR we will setup below */   /* The PDBR we will setup below */
   struct x86_pdbr cr3;     struct x86_pdbr cr3;  
Line 286 
Line 287 
   /* Map a page for the PT if necessary */   /* Map a page for the PT if necessary */
   if (! pd[index_in_pd].present)   if (! pd[index_in_pd].present)
     {     {
        
       /* No : allocate a new one */       /* No : allocate a new one */
       sos_paddr_t pt_ppage       sos_paddr_t pt_ppage
         = sos_physmem_ref_physpage_new(! (flags & SOS_VM_MAP_ATOMIC));         = sos_physmem_ref_physpage_new(! (flags & SOS_VM_MAP_ATOMIC));
Line 297 
Line 299 
       pd[index_in_pd].present  = TRUE;       pd[index_in_pd].present  = TRUE;
       pd[index_in_pd].write    = 1; /* Ignored in supervisor mode, see       pd[index_in_pd].write    = 1; /* Ignored in supervisor mode, see
                                        Intel vol 3 section 4.12 */                                        Intel vol 3 section 4.12 */
       pd[index_in_pd].user     |= (is_user_page)?1:0;       pd[index_in_pd].user     = (is_user_page)?1:0;
              
       /*       /*
Line 380 
Line 382 
   /* Reclaim this entry in the PT, which may free the PT */   /* 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);   pt_unref_retval = sos_physmem_unref_physpage(pd[index_in_pd].pt_paddr << 12);
   SOS_ASSERT_FATAL(pt_unref_retval >= 0);   SOS_ASSERT_FATAL(pt_unref_retval >= 0);
   if (pt_unref_retval > 0)   if (pt_unref_retval == TRUE)
     {     {
       /* Release the PDE */       union { struct x86_pde pde; sos_ui32_t ui32; } u;
       memset(pd + index_in_pd, 0x0, sizeof(struct x86_pde)); 
        /*
         * Reset the PDE
         */
  
        /* Mark the PDE as unavailable */
        u.ui32 = 0;
  
        /* Update the PD */
        pd[index_in_pd] = u.pde;
       /* Update the TLB */       /* Update the TLB */
       invlpg(pt);       invlpg(pt);
Line 452 
Line 463 
  
   return (pt[index_in_pt].paddr << 12) + offset_in_page;   return (pt[index_in_pt].paddr << 12) + offset_in_page;
 } }
  
 

/tmp/sos-code-article6/hwcore/paging.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/paging.h (2005-03-02 17:22:08.000000000 +0100 )
Line 37 
Line 37 
 #include <sos/types.h> #include <sos/types.h>
 #include <sos/errno.h> #include <sos/errno.h>
  
  
  /**
   * Basic SOS virtual memory organization
   */
  /** Frontier between kernel and user space virtual addresses */
  #define SOS_PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB */
  #define SOS_PAGING_TOP_USER_ADDRESS (0xFFFFFFFF) /* 4GB */
  
  /** Length of the space reserved for the mirroring in the kernel
      virtual space */
  #define SOS_PAGING_MIRROR_SIZE  (1 << 22)  /* 1 PD = 1024 Page Tables = 4MB */
  
  /** Virtual address where the mirroring takes place */
  #define SOS_PAGING_MIRROR_VADDR \
     (SOS_PAGING_BASE_USER_ADDRESS - SOS_PAGING_MIRROR_SIZE)
  
  
 /** /**
  * sos_paging_map flags  * sos_paging_map flags
  */  */
Line 45 
Line 62 
 #define SOS_VM_MAP_PROT_READ  (1<<0) #define SOS_VM_MAP_PROT_READ  (1<<0)
 #define SOS_VM_MAP_PROT_WRITE (1<<1) #define SOS_VM_MAP_PROT_WRITE (1<<1)
 /* EXEC not supported */ /* EXEC not supported */
  
 /** Mapping a page may involve an physical page allocation (for a new /** Mapping a page may involve an physical page allocation (for a new
     PT), hence may potentially block */     PT), hence may potentially block */
 #define SOS_VM_MAP_ATOMIC     (1<<31) #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  * Setup initial page directory structure where the kernel is
Line 62 
Line 75 
  * text to be printed to the console. Finally, this routine installs  * text to be printed to the console. Finally, this routine installs
  * the whole configuration into the MMU.  * the whole configuration into the MMU.
  */  */
 sos_ret_t sos_paging_setup(sos_paddr_t identity_mapping_base, sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base,
                            sos_paddr_t identity_mapping_top);                                      sos_paddr_t identity_mapping_top);
 /** /**
  * Map the given physical page at the given virtual address in the  * Map the given physical page at the given virtual address in the
Line 116 
Line 129 
 #define sos_paging_check_present(vaddr) \ #define sos_paging_check_present(vaddr) \
   (sos_paging_get_paddr(vaddr) != NULL)   (sos_paging_get_paddr(vaddr) != NULL)
  
  
  
 

/tmp/sos-code-article6/hwcore/segment.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/segment.h (2005-03-02 17:22:08.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The SOS Team /* Copyright (C) 2004  The SOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 30 
Line 29 
  * @see Intel x86 doc, vol 3 chapter 3.  * @see Intel x86 doc, vol 3 chapter 3.
  */  */
  
 #include <sos/types.h> 
 /* /*
  * Global segment selectors (GDT) for SOS/x86.  * Global segment selectors (GDT) for SOS/x86.
  *  *
  * @see gdt.h  * @see gdt.h
  */  */
 #define SOS_SEG_NULL  0 /* NULL segment, unused by the procesor */ #define SOS_SEG_NULL       0 /* NULL segment, unused by the procesor */
 #define SOS_SEG_KCODE 1 /* Kernel code segment */ #define SOS_SEG_KCODE      1 /* Kernel code segment */
 #define SOS_SEG_KDATA 2 /* Kernel data segment */ #define SOS_SEG_KDATA      2 /* Kernel data segment */
  
  #ifndef ASM_SOURCE
 /** /**
  * Helper macro that builds a segment register's value  * Helper macro that builds a segment register's value
  */  */
Line 49 
Line 48 
   (  (((desc_privilege) & 0x3)  << 0) \   (  (((desc_privilege) & 0x3)  << 0) \
    | (((in_ldt)?1:0)            << 2) \    | (((in_ldt)?1:0)            << 2) \
    | ((seg_index)               << 3) )    | ((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
  
  
 /* /*
  
 

/tmp/sos-code-article6/sos/assert.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/assert.c (2005-03-02 17:22:09.000000000 +0100 )
Line 34 
Line 34 
   va_end(ap);   va_end(ap);
  
   sos_bochs_putstring(buff); sos_bochs_putstring("\n");   sos_bochs_putstring(buff); sos_bochs_putstring("\n");
   sos_x86_videomem_putstring(24, 0,   sos_x86_videomem_putstring(23, 0,
                              | SOS_X86_VIDEO_FG_LTRED , buff);                              | SOS_X86_VIDEO_FG_LTRED , buff);
  
  
 

/tmp/sos-code-article6/sos/assert.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/assert.h (2005-03-02 17:22:08.000000000 +0100 )
Line 35 
Line 35 
    })    })
  
  
  #define SOS_FATAL_ERROR(fmt,args...) \
     ({ \
        sos_display_fatal_error("%s@%s:%d FATAL: " fmt, \
                                __PRETTY_FUNCTION__, __FILE__, __LINE__, \
                                ##args); \
     })
  
 #endif /* _SOS_ASSERT_H_ */ #endif /* _SOS_ASSERT_H_ */
  
 

/tmp/sos-code-article6/sos/errno.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/errno.h (2005-03-02 17:22:09.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The SOS Team /* Copyright (C) 2004  The SOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 31 
Line 30 
 #define SOS_ENOSUP 2   /* Operation not supported */ #define SOS_ENOSUP 2   /* Operation not supported */
 #define SOS_ENOMEM 3   /* No available memory */ #define SOS_ENOMEM 3   /* No available memory */
 #define SOS_EBUSY  4   /* Object or device still in use */ #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_EFATAL 255 /* Internal fatal error */ #define SOS_EFATAL 255 /* Internal fatal error */
  
 /* A negative value means that an error occured.  For /* A negative value means that an error occured.  For
  
 

/tmp/sos-code-article6/sos/klibc.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/klibc.c (2005-03-02 17:22:09.000000000 +0100 )
Line 142 
Line 142 
 } }
  
  
  static unsigned long int _random_seed = 93186752;
  
  /**
   * The following code is borrowed from Glenn Rhoads.
   * http://remus.rutgers.edu/~rhoads/Code/code.html
   * License to be defined...
   */
  unsigned long int random (void)
  {
  /* The following parameters are recommended settings based on research
     uncomment the one you want. */
  
  /* For RAND_MAX == 4294967291 */
     static unsigned int a = 1588635695, q = 2, r = 1117695901;
  /* static unsigned int a = 1223106847, m = 4294967291U, q = 3, r = 625646750;*/
  /* static unsigned int a = 279470273, m = 4294967291U, q = 15, r = 102913196;*/
  
  /* For RAND_MAX == 2147483647 */
  /* static unsigned int a = 1583458089, m = 2147483647, q = 1, r = 564025558; */
  /* static unsigned int a = 784588716, m = 2147483647, q = 2, r = 578306215;  */
  /* static unsigned int a = 16807, m = 2147483647, q = 127773, r = 2836;      */
  /* static unsigned int a = 950706376, m = 2147483647, q = 2, r = 246070895;  */
  
     _random_seed = a*(_random_seed % q) - r*(_random_seed / q);
     return _random_seed;
  }
  
  
  void srandom (unsigned long int seed)
  {
    _random_seed = seed;
  }
  
  
 /* I (d2) borrowed and rewrote this for Nachos/INSA Rennes. Thanks to /* I (d2) borrowed and rewrote this for Nachos/INSA Rennes. Thanks to
    them for having kindly allowed me to do so. */    them for having kindly allowed me to do so. */
 int vsnprintf(char *buff, sos_size_t len, const char * format, va_list ap) int vsnprintf(char *buff, sos_size_t len, const char * format, va_list ap)
  
 

/tmp/sos-code-article6/sos/klibc.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/klibc.h (2005-03-02 17:22:09.000000000 +0100 )
Line 85 
Line 85 
 int snprintf(char *, sos_size_t, const char *, /*args*/ ...) int snprintf(char *, sos_size_t, const char *, /*args*/ ...)
   __attribute__ ((format (printf, 3, 4)));   __attribute__ ((format (printf, 3, 4)));
  
  
  /*
   * Pseudo-random generation functions. Useful to do some coverage
   * tests.
   */
  
  /* Amplitude of the random number generation */
  #define RAND_MAX 4294967291U
  
  /* Pseudo-random number generation (MT unsafe) */
  unsigned long int random (void);
  
  /* Set random seed (MT unsafe) */
  void srandom (unsigned long int seed);
  
 #endif /* _SOS_KLIBC_H_ */ #endif /* _SOS_KLIBC_H_ */
  
 

/tmp/sos-code-article6/sos/kmalloc.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmalloc.c (2005-03-02 17:22:09.000000000 +0100 )
Line 50 
Line 50 
   };   };
  
  
 sos_ret_t sos_kmalloc_setup() sos_ret_t sos_kmalloc_subsystem_setup()
   int i;   int i;
   for (i = 0 ; kmalloc_cache[i].object_size != 0 ; i ++)   for (i = 0 ; kmalloc_cache[i].object_size != 0 ; i ++)
  
 

/tmp/sos-code-article6/sos/kmalloc.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmalloc.h (2005-03-02 17:22:09.000000000 +0100 )
Line 32 
Line 32 
 /** /**
  * Iniatilize the kmalloc subsystem, ie pre-allocate a series of caches.  * Iniatilize the kmalloc subsystem, ie pre-allocate a series of caches.
  */  */
 sos_ret_t sos_kmalloc_setup(void); sos_ret_t sos_kmalloc_subsystem_setup(void);
 /* /*
  * sos_kmalloc flags  * sos_kmalloc flags
  
 

/tmp/sos-code-article6/sos/kmem_slab.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmem_slab.c (2005-03-02 17:22:09.000000000 +0100 )
Line 429 
Line 429 
  
  
 struct sos_kslab_cache * struct sos_kslab_cache *
 sos_kmem_cache_setup_prepare(sos_vaddr_t kernel_core_base, sos_kmem_cache_subsystem_setup_prepare(sos_vaddr_t kernel_core_base,
                              sos_vaddr_t kernel_core_top,                                        sos_vaddr_t kernel_core_top,
                              sos_size_t  sizeof_struct_range,                                        sos_size_t  sizeof_struct_range,
                              /* results */                                        /* results */
                              struct sos_kslab **first_struct_slab_of_caches,                                        struct sos_kslab **first_struct_slab_of_caches,
                              sos_vaddr_t *first_slab_of_caches_base,                                        sos_vaddr_t *first_slab_of_caches_base,
                              sos_count_t *first_slab_of_caches_nb_pages,                                        sos_count_t *first_slab_of_caches_nb_pages,
                              struct sos_kslab **first_struct_slab_of_ranges,                                        struct sos_kslab **first_struct_slab_of_ranges,
                              sos_vaddr_t *first_slab_of_ranges_base,                                        sos_vaddr_t *first_slab_of_ranges_base,
                              sos_count_t *first_slab_of_ranges_nb_pages)                                        sos_count_t *first_slab_of_ranges_nb_pages)
   int i;   int i;
   sos_ret_t   retval;   sos_ret_t   retval;
Line 545 
Line 545 
  
  
 sos_ret_t sos_ret_t
 sos_kmem_cache_setup_commit(struct sos_kslab *first_struct_slab_of_caches, sos_kmem_cache_subsystem_setup_commit(struct sos_kslab *first_struct_slab_of_caches,
                             struct sos_kmem_range *first_range_of_caches,                                       struct sos_kmem_range *first_range_of_caches,
                             struct sos_kslab *first_struct_slab_of_ranges,                                       struct sos_kslab *first_struct_slab_of_ranges,
                             struct sos_kmem_range *first_range_of_ranges)                                       struct sos_kmem_range *first_range_of_ranges)
   first_struct_slab_of_caches->range = first_range_of_caches;   first_struct_slab_of_caches->range = first_range_of_caches;
   first_struct_slab_of_ranges->range = first_range_of_ranges;   first_struct_slab_of_ranges->range = first_range_of_ranges;
  
 

/tmp/sos-code-article6/sos/kmem_slab.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmem_slab.h (2005-03-02 17:22:09.000000000 +0100 )
Line 117 
Line 117 
  * @return the cache of kmem_range immediatly usable  * @return the cache of kmem_range immediatly usable
  */  */
 struct sos_kslab_cache * struct sos_kslab_cache *
 sos_kmem_cache_setup_prepare(sos_vaddr_t kernel_core_base, sos_kmem_cache_subsystem_setup_prepare(sos_vaddr_t kernel_core_base,
                              sos_vaddr_t kernel_core_top,                                        sos_vaddr_t kernel_core_top,
                              sos_size_t  sizeof_struct_range,                                        sos_size_t  sizeof_struct_range,
                              /* results */                                        /* results */
                              struct sos_kslab **first_struct_slab_of_caches,                                        struct sos_kslab **first_struct_slab_of_caches,
                              sos_vaddr_t *first_slab_of_caches_base,                                        sos_vaddr_t *first_slab_of_caches_base,
                              sos_count_t *first_slab_of_caches_nb_pages,                                        sos_count_t *first_slab_of_caches_nb_pages,
                              struct sos_kslab **first_struct_slab_of_ranges,                                        struct sos_kslab **first_struct_slab_of_ranges,
                              sos_vaddr_t *first_slab_of_ranges_base,                                        sos_vaddr_t *first_slab_of_ranges_base,
                              sos_count_t *first_slab_of_ranges_nb_pages);                                        sos_count_t *first_slab_of_ranges_nb_pages);
 /** /**
  * Update the configuration of the cache subsystem once the vmm  * Update the configuration of the cache subsystem once the vmm
  * subsystem has been fully initialized  * subsystem has been fully initialized
  */  */
 sos_ret_t sos_ret_t
 sos_kmem_cache_setup_commit(struct sos_kslab *first_struct_slab_of_caches, sos_kmem_cache_subsystem_setup_commit(struct sos_kslab *first_struct_slab_of_caches,
                             struct sos_kmem_range *first_range_of_caches,                                       struct sos_kmem_range *first_range_of_caches,
                             struct sos_kslab *first_struct_slab_of_ranges,                                       struct sos_kslab *first_struct_slab_of_ranges,
                             struct sos_kmem_range *first_range_of_ranges);                                       struct sos_kmem_range *first_range_of_ranges);
  
 /* /*
  
 

/tmp/sos-code-article6/sos/kmem_vmm.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmem_vmm.c (2005-03-02 17:22:09.000000000 +0100 )
Line 121 
Line 121 
  
   /* First: try to retrieve the physical page mapped at this address */   /* 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));   sos_paddr_t ppage_paddr = SOS_PAGE_ALIGN_INF(sos_paging_get_paddr(vaddr));
  
   if (ppage_paddr)   if (ppage_paddr)
     {     {
       range = sos_physmem_get_kmem_range(ppage_paddr);       range = sos_physmem_get_kmem_range(ppage_paddr);
Line 203 
Line 204 
 } }
  
  
 sos_ret_t sos_kmem_vmm_setup(sos_vaddr_t kernel_core_base, sos_ret_t
  sos_kmem_vmm_subsystem_setup(sos_vaddr_t kernel_core_base,
                              sos_vaddr_t bootstrap_stack_bottom_vaddr,                              sos_vaddr_t bootstrap_stack_bottom_vaddr,
                              sos_vaddr_t bootstrap_stack_top_vaddr)                              sos_vaddr_t bootstrap_stack_top_vaddr)
Line 221 
Line 223 
   list_init(kmem_used_range_list);   list_init(kmem_used_range_list);
  
   kmem_range_cache   kmem_range_cache
     = sos_kmem_cache_setup_prepare(kernel_core_base,     = sos_kmem_cache_subsystem_setup_prepare(kernel_core_base,
                                    kernel_core_top,                                              kernel_core_top,
                                    sizeof(struct sos_kmem_range),                                              sizeof(struct sos_kmem_range),
                                    & first_struct_slab_of_caches,                                              & first_struct_slab_of_caches,
                                    & first_slab_of_caches_base,                                              & first_slab_of_caches_base,
                                    & first_slab_of_caches_nb_pages,                                              & first_slab_of_caches_nb_pages,
                                    & first_struct_slab_of_ranges,                                              & first_struct_slab_of_ranges,
                                    & first_slab_of_ranges_base,                                              & first_slab_of_ranges_base,
                                    & first_slab_of_ranges_nb_pages);                                              & first_slab_of_ranges_nb_pages);
  
   /* Mark virtual addresses 16kB - Video as FREE */   /* Mark virtual addresses 16kB - Video as FREE */
Line 306 
Line 308 
   /* Update the cache subsystem so that the artificially-created   /* Update the cache subsystem so that the artificially-created
      caches of caches and ranges really behave like *normal* caches (ie      caches of caches and ranges really behave like *normal* caches (ie
      those allocated by the normal slab API) */      those allocated by the normal slab API) */
   sos_kmem_cache_setup_commit(first_struct_slab_of_caches,   sos_kmem_cache_subsystem_setup_commit(first_struct_slab_of_caches,
                               first_range_of_caches,                                         first_range_of_caches,
                               first_struct_slab_of_ranges,                                         first_struct_slab_of_ranges,
                               first_range_of_ranges);                                         first_range_of_ranges);
   return SOS_OK;   return SOS_OK;
 } }
  
 

/tmp/sos-code-article6/sos/kmem_vmm.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/kmem_vmm.h (2005-03-02 17:22:09.000000000 +0100 )
Line 44 
Line 44 
  * as "used", and the 0..SOS_KMEM_VMM_BASE virtual addresses as marked  * as "used", and the 0..SOS_KMEM_VMM_BASE virtual addresses as marked
  * as "used" too (to detect incorrect pointer dereferences).  * as "used" too (to detect incorrect pointer dereferences).
  */  */
 sos_ret_t sos_kmem_vmm_setup(sos_vaddr_t kernel_core_base, sos_ret_t
                              sos_vaddr_t kernel_core_top, sos_kmem_vmm_subsystem_setup(sos_vaddr_t kernel_core_base_vaddr,
                              sos_vaddr_t bootstrap_stack_bottom_addr,                              sos_vaddr_t kernel_core_top_vaddr,
                              sos_vaddr_t bootstrap_stack_top_addr);                              sos_vaddr_t bootstrap_stack_bottom_vaddr,
                               sos_vaddr_t bootstrap_stack_top_vaddr);
  
 /* /*
  
 

/tmp/sos-code-article6/sos/ksynch.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/ksynch.c (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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 "ksynch.h"
  
  
  sos_ret_t sos_ksema_init(struct sos_ksema *sema, const char *name,
                           int initial_value)
  {
    sema->value = initial_value;
    return sos_kwaitq_init(& sema->kwaitq, name);
  }
  
  
  sos_ret_t sos_ksema_dispose(struct sos_ksema *sema)
  {
    return sos_kwaitq_dispose(& sema->kwaitq);
  }
  
  
  sos_ret_t sos_ksema_down(struct sos_ksema *sema,
                           struct sos_time *timeout)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = SOS_OK;
  
    sema->value --;
    if (sema->value < 0)
      {
        /* Wait for somebody to wake us */
        retval = sos_kwaitq_wait(& sema->kwaitq, timeout);
  
        /* Something wrong happened (timeout, external wakeup, ...) ? */
        if (SOS_OK != retval)
          {
            /* Yes: pretend we did not ask for the semaphore */
            sema->value ++;
          }
      }
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_ret_t sos_ksema_trydown(struct sos_ksema *sema)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
  
    /* Can we take the semaphore without blocking ? */
    if (sema->value >= 1)
      {
        /* Yes: we take it now */
        sema->value --;      
        retval = SOS_OK;
      }
    else
      {
        /* No: we signal it */
        retval = -SOS_EBUSY;
      }
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_ret_t sos_ksema_up(struct sos_ksema *sema)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
  
    sema->value ++;
    retval = sos_kwaitq_wakeup(& sema->kwaitq, 1, SOS_OK);
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_ret_t sos_kmutex_init(struct sos_kmutex *mutex, const char *name)
  {
    mutex->owner = NULL;
    return sos_kwaitq_init(& mutex->kwaitq, name);
  }
  
  
  sos_ret_t sos_kmutex_dispose(struct sos_kmutex *mutex)
  {
    return sos_kwaitq_dispose(& mutex->kwaitq);
  }
  
  
  /*
   * Implementation based on ownership transfer (ie no while()
   * loop). The only assumption is that the thread awoken by
   * kmutex_unlock is not suppressed before effectively waking up: in
   * that case the mutex will be forever locked AND unlockable (by
   * nobody other than the owner, but this is not natural since this
   * owner already issued an unlock()...). The same problem happens with
   * the semaphores, but in a less obvious manner.
   */
  sos_ret_t sos_kmutex_lock(struct sos_kmutex *mutex,
                            struct sos_time *timeout)
  {
    __label__ exit_kmutex_lock;
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = SOS_OK;
  
    /* Mutex already owned ? */
    if (NULL != mutex->owner)
      {
        /* Owned by us or by someone else ? */
        if (sos_thread_get_current() == mutex->owner)
          {
            /* Owned by us: do nothing */
            retval = -SOS_EBUSY;
            goto exit_kmutex_lock;
          }
  
        /* Wait for somebody to wake us */
        retval = sos_kwaitq_wait(& mutex->kwaitq, timeout);
  
        /* Something wrong happened ? */
        if (SOS_OK != retval)
          {
            goto exit_kmutex_lock;
          }
      }
  
    /* Ok, the mutex is available to us: take it */
    mutex->owner = sos_thread_get_current();
  
   exit_kmutex_lock:
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_ret_t sos_kmutex_trylock(struct sos_kmutex *mutex)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
  
    /* Mutex available to us ? */
    if (NULL == mutex->owner)
      {
        /* Great ! Take it now */
        mutex->owner = sos_thread_get_current();
  
        retval = SOS_OK;
      }
    else
      {
        /* No: signal it */
        retval = -SOS_EBUSY;
      }
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_ret_t sos_kmutex_unlock(struct sos_kmutex *mutex)
  {
    sos_ui32_t flags;
    sos_ret_t  retval;
  
    sos_disable_IRQs(flags);
  
    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
      {
        /*
         * 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;
  }
  
 

/tmp/sos-code-article6/sos/ksynch.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/ksynch.h (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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_KSYNCH_H_
  #define _SOS_KSYNCH_H_
  
  
  /**
   * @file synch.h
   *
   * Common kernel synchronisation primitives.
   */
  
  
  #include <sos/errno.h>
  #include <sos/kwaitq.h>
  
  
  /* ====================================================================
   * Kernel semaphores, NON-recursive
   */
  
  
  /**
   * The structure of a (NON-RECURSIVE) kernel Semaphore
   */
  struct sos_ksema
  {
    int value;
    struct sos_kwaitq kwaitq;
  };
  
  
  /*
   * Initialize a kernel semaphore structure with the given name
   *
   * @param name Name of the semaphore (for debugging purpose only; safe
   * [deep copied])
   *
   * @param initial_value The value of the semaphore before any up/down
   */
  sos_ret_t sos_ksema_init(struct sos_ksema *sema, const char *name,
                           int initial_value);
  
  
  /*
   * De-initialize a kernel semaphore
   *
   * @return -SOS_EBUSY when semaphore could not be de-initialized
   * because at least a thread is in the waitq.
   */
  sos_ret_t sos_ksema_dispose(struct sos_ksema *sema);
  
  
  /*
   * Enters the semaphore
   *
   * @param timeout Maximum time to wait for the semaphore. Or NULL for
   * "no limit". Updated on return to reflect the time remaining (0 when
   * timeout has been triggered)
   *
   * @return -SOS_EINTR when timeout was triggered or when another waitq
   * woke us up.
   *
   * @note This is a BLOCKING FUNCTION
   */
  sos_ret_t sos_ksema_down(struct sos_ksema *sema,
                           struct sos_time *timeout);
  
  
  /*
   * Try to enter the semaphore without blocking.
   *
   * @return -SOS_EBUSY when locking the semaphore would block
   */
  sos_ret_t sos_ksema_trydown(struct sos_ksema *sema);
  
  
  /**
   * Increments the semaphore's value, eventually waking up a thread
   */
  sos_ret_t sos_ksema_up(struct sos_ksema *sema);
  
  
  
  /* ====================================================================
   * Kernel mutex (ie binary semaphore with strong ownership),
   * NON-recursive !
   */
  
  
  /**
   * The structure of a (NON-RECURSIVE) kernel Mutex
   */
  struct sos_kmutex
  {
    struct sos_thread  *owner;
    struct sos_kwaitq  kwaitq;
  };
  
  
  /*
   * Initialize a kernel mutex structure with the given name
   *
   * @param name Name of the mutex (for debugging purpose only; safe
   * [deep copied])
   *
   * @param initial_value The value of the mutex before any up/down
   */
  sos_ret_t sos_kmutex_init(struct sos_kmutex *mutex, const char *name);
  
  
  /*
   * De-initialize a kernel mutex
   *
   * @return -SOS_EBUSY when mutex could not be de-initialized
   * because at least a thread is in the waitq.
   */
  sos_ret_t sos_kmutex_dispose(struct sos_kmutex *mutex);
  
  
  /*
   * Lock the mutex. If the same thread multiply locks the same mutex,
   * it won't hurt (no deadlock, return value = -SOS_EBUSY).
   *
   * @param timeout Maximum time to wait for the mutex. Or NULL for "no
   * limit". Updated on return to reflect the time remaining (0 when
   * timeout has been triggered)
   *
   * @return -SOS_EINTR when timeout was triggered or when another waitq
   * woke us up, -SOS_EBUSY when the thread already owns the mutex.
   *
   * @note This is a BLOCKING FUNCTION
   */
  sos_ret_t sos_kmutex_lock(struct sos_kmutex *mutex,
                            struct sos_time *timeout);
  
  
  /*
   * Try to lock the mutex without blocking.
   *
   * @return -SOS_EBUSY when locking the mutex would block
   */
  sos_ret_t sos_kmutex_trylock(struct sos_kmutex *mutex);
  
  
  /**
   * Unlock the mutex, eventually waking up a thread
   *
   * @return -SOS_EPERM when the calling thread is NOT the owner of the
   * mutex
   */
  sos_ret_t sos_kmutex_unlock(struct sos_kmutex *mutex);
  
  
  #endif /* _SOS_KSYNCH_H_ */
  
 

/tmp/sos-code-article6/sos/kwaitq.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/kwaitq.c (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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/klibc.h>
  #include <sos/list.h>
  #include <sos/assert.h>
  #include <hwcore/irq.h>
  
  #include "kwaitq.h"
  
  
  sos_ret_t sos_kwaitq_init(struct sos_kwaitq *kwq,
                            const char *name)
  {
    memset(kwq, 0x0, sizeof(struct sos_kwaitq));
  
  #ifdef SOS_KWQ_DEBUG
    if (! name)
      name = "<unknown>";
    strzcpy(kwq->name, name, SOS_KWQ_DEBUG_MAX_NAMELEN);
  #endif
    list_init_named(kwq->waiting_list,
                    prev_entry_in_kwaitq, next_entry_in_kwaitq);
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_kwaitq_dispose(struct sos_kwaitq *kwq)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    if (list_is_empty_named(kwq->waiting_list,
                            prev_entry_in_kwaitq, next_entry_in_kwaitq))
      retval = SOS_OK;
    else
      retval = -SOS_EBUSY;
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  sos_bool_t sos_kwaitq_is_empty(const struct sos_kwaitq *kwq)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = list_is_empty_named(kwq->waiting_list,
                                 prev_entry_in_kwaitq, next_entry_in_kwaitq);
  
    sos_restore_IRQs(flags);
    return retval;  
  }
  
  
  sos_ret_t sos_kwaitq_init_entry(struct sos_kwaitq_entry *kwq_entry)
  {
    memset(kwq_entry, 0x0, sizeof(struct sos_kwaitq_entry));
    kwq_entry->thread = sos_thread_get_current();
    return SOS_OK;
  }
  
  
  /** Internal helper function equivalent to sos_kwaitq_add_entry(), but
      without interrupt protection scheme, and explicit priority
      ordering */
  inline static sos_ret_t _kwaitq_add_entry(struct sos_kwaitq *kwq,
                                            struct sos_kwaitq_entry *kwq_entry)
  {
    /* This entry is already added in the kwaitq ! */
    SOS_ASSERT_FATAL(NULL == kwq_entry->kwaitq);
  
    /* sos_kwaitq_init_entry() has not been called ?! */
    SOS_ASSERT_FATAL(NULL != kwq_entry->thread);
  
    /* (Re-)Initialize wakeup status of the entry */
    kwq_entry->wakeup_triggered = FALSE;
    kwq_entry->wakeup_status    = SOS_OK;
  
    /* Add the thread in the list */
    list_add_tail_named(kwq->waiting_list, kwq_entry,
                        prev_entry_in_kwaitq, next_entry_in_kwaitq);
  
    /* Update the list of waitqueues for the thread */
    list_add_tail_named(kwq_entry->thread->kwaitq_list, kwq_entry,
                        prev_entry_for_thread, next_entry_for_thread);
  
    kwq_entry->kwaitq = kwq;
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_kwaitq_add_entry(struct sos_kwaitq *kwq,
                                 struct sos_kwaitq_entry *kwq_entry)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = _kwaitq_add_entry(kwq, kwq_entry);
    sos_restore_IRQs(flags);
  
    return retval;
  }
  
  
  /** Internal helper function equivalent to sos_kwaitq_remove_entry(),
      but without interrupt protection scheme */
  inline static sos_ret_t
  _kwaitq_remove_entry(struct sos_kwaitq *kwq,
                       struct sos_kwaitq_entry *kwq_entry)
  {
    SOS_ASSERT_FATAL(kwq_entry->kwaitq == kwq);
  
    list_delete_named(kwq->waiting_list, kwq_entry,
                      prev_entry_in_kwaitq, next_entry_in_kwaitq);
  
    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;
  }
  
  
  sos_ret_t sos_kwaitq_remove_entry(struct sos_kwaitq *kwq,
                                    struct sos_kwaitq_entry *kwq_entry)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = _kwaitq_remove_entry(kwq, kwq_entry);
    sos_restore_IRQs(flags);
  
    return retval;
  }
  
  
  sos_ret_t sos_kwaitq_wait(struct sos_kwaitq *kwq,
                            struct sos_time *timeout)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
    struct sos_kwaitq_entry kwq_entry;
  
    sos_kwaitq_init_entry(& kwq_entry);
  
    sos_disable_IRQs(flags);
  
    retval = _kwaitq_add_entry(kwq, & kwq_entry);
  
    /* Wait for wakeup or timeout */
    sos_thread_sleep(timeout);
    /* Woken up ! */
  
    /* Sleep delay elapsed ? */
    if (! kwq_entry.wakeup_triggered)
      {
        /* Yes (timeout occured, or wakeup on another waitqueue): remove
           the waitq entry by ourselves */
        _kwaitq_remove_entry(kwq, & kwq_entry);
        retval = -SOS_EINTR;
      }
    else
      {
        retval = kwq_entry.wakeup_status;
      }
    
    sos_restore_IRQs(flags);
  
    /* We were correctly awoken: position return status */
    return retval;
  }
  
  
  sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
                              unsigned int nb_threads,
                              sos_ret_t wakeup_status)
  {
    sos_ui32_t flags;
  
    sos_disable_IRQs(flags);
  
    /* Wake up as much threads waiting in waitqueue as possible (up to
       nb_threads), scanning the list in FIFO order */
    while (! list_is_empty_named(kwq->waiting_list,
                                 prev_entry_in_kwaitq, next_entry_in_kwaitq))
      {
        struct sos_kwaitq_entry *kwq_entry
          = list_get_head_named(kwq->waiting_list,
                                prev_entry_in_kwaitq, next_entry_in_kwaitq);
  
        /* Enough threads woken up ? */
        if (nb_threads <= 0)
          break;
  
        /*
         * Ok: wake up the thread for this entry
         */
  
        /* Thread already woken up ? */
        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
               would result in an inconsistent configuration (currently
               running thread marked as "waiting for CPU"...). */
            continue;
          }
        else
          {
            /* No => wake it up now. */
            sos_sched_set_ready(kwq_entry->thread);
          }
  
        /* Remove this waitq entry */
        _kwaitq_remove_entry(kwq, kwq_entry);
        kwq_entry->wakeup_triggered = TRUE;
        kwq_entry->wakeup_status    = wakeup_status;
  
        /* Next iteration... */
        nb_threads --;
      }
  
    sos_restore_IRQs(flags);
  
    return SOS_OK;
  }
  
 

/tmp/sos-code-article6/sos/kwaitq.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/kwaitq.h (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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_KWAITQ_H_
  #define _SOS_KWAITQ_H_
  
  #include <sos/errno.h>
  #include <sos/thread.h>
  #include <sos/time.h>
  #include <sos/sched.h>
  
  
  /**
   * @kwaitq.h
   *
   * Low-level functions to manage queues of threads waiting for a
   * resource. These functions are public. For higher-level
   * synchronization primitives such as mutex, semaphores, conditions,
   * ... prefer looking at the corresponding libraries.
   */
  
  
  /**
   * Define this if you want to know the names of the kwaitq
   */
  // #define SOS_KWQ_DEBUG
  
  
  /* Forward declaration */
  struct sos_kwaitq_entry;
  
  
  /**
   * Definition of a waitqueue. In a kwaitq, the threads are ordererd in
   * FIFO order.
   */
  struct sos_kwaitq
  {
  #ifdef SOS_KWQ_DEBUG
  # define SOS_KWQ_DEBUG_MAX_NAMELEN 32
    char name[SOS_KWQ_DEBUG_MAX_NAMELEN];
  #endif
    struct sos_kwaitq_entry *waiting_list;
  };
  
  
  /**
   * Definition of an entry for a thread waiting in the waitqueue
   */
  struct sos_kwaitq_entry
  {
    /** The thread associted with this entry */
    struct sos_thread *thread;
  
    /** The kwaitqueue this entry belongs to */
    struct sos_kwaitq *kwaitq;
  
    /** TRUE when somebody woke up this entry */
    sos_bool_t wakeup_triggered;
  
    /** The status of wakeup for this entry. @see wakeup_status argument
        of sos_kwaitq_wakeup() */
    sos_ret_t wakeup_status;
  
    /** Other entries in this kwaitqueue */
    struct sos_kwaitq_entry *prev_entry_in_kwaitq, *next_entry_in_kwaitq;
  
    /** Other entries for the thread */
    struct sos_kwaitq_entry *prev_entry_for_thread, *next_entry_for_thread;  
  };
  
  
  /**
   * Initialize an empty waitqueue.
   *
   * @param name Used only if SOS_KWQ_DEBUG is defined (safe [deep
   * copied])
   */
  sos_ret_t sos_kwaitq_init(struct sos_kwaitq *kwq,
                            const char *name);
  
  
  /**
   * Release a waitqueue, making sure that no thread is in it.
   *
   * @return -SOS_EBUSY in case a thread is still in the waitqueue.
   */
  sos_ret_t sos_kwaitq_dispose(struct sos_kwaitq *kwq);
  
  
  /**
   * Return whether there are no threads in the waitq
   */
  sos_bool_t sos_kwaitq_is_empty(const struct sos_kwaitq *kwq);
  
  
  /**
   * Initialize a waitqueue entry. Mainly consists in updating the
   * "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.
   */
  sos_ret_t sos_kwaitq_init_entry(struct sos_kwaitq_entry *kwq_entry);
  
  
  /**
   * Add an entry (previously initialized with sos_kwaitq_init_entry())
   * in the given waitqueue.
   *
   * @note: No state change/context switch can occur here ! Among other
   * things: the current executing thread is not preempted.
   */
  sos_ret_t sos_kwaitq_add_entry(struct sos_kwaitq *kwq,
                                 struct sos_kwaitq_entry *kwq_entry);
  
  
  /**
   * Remove the given kwaitq_entry from the kwaitq.
   *
   * @note: No state change/context switch can occur here ! Among other
   * things: the thread associated with the entry is not necessarilly
   * the same as the one currently running, and does not preempt the
   * current running thread if they are different.
   */
  sos_ret_t sos_kwaitq_remove_entry(struct sos_kwaitq *kwq,
                                    struct sos_kwaitq_entry *kwq_entry);
  
  
  /**
   * Helper function to make the current running thread block in the
   * given kwaitq, waiting to be woken up by somedy else or by the given
   * timeout. It calls the sos_kwaitq_add_entry() and
   * sos_kwaitq_remove_entry().
   *
   * @param timeout The desired timeout (can be NULL => wait for
   * ever). It is updated by the function to reflect the remaining
   * timeout in case the thread has been woken-up prior to its
   * expiration.
   *
   * @return -SOS_EINTR when the thread is resumed while it has not be
   * explicitely woken up by someone calling sos_kwaitq_wakeup() upon
   * the same waitqueue... This can only happen 1/ if the timeout
   * expired, or 2/ if the current thread is also in another kwaitq
   * different to "kwq". Otherwise return the value set by
   * sos_kwaitq_wakeup(). The timeout remaining is updated in timeout.
   *
   * @note This is a BLOCKING FUNCTION
   */
  sos_ret_t sos_kwaitq_wait(struct sos_kwaitq *kwq,
                            struct sos_time *timeout);
  
  
  /**
   * Wake up as much as nb_thread threads (SOS_KWQ_WAKEUP_ALL to wake
   * up all threads) in the kwaitq kwq, in FIFO order.
   *
   * @param wakeup_status The value returned by sos_kwaitq_wait() when
   * the thread will effectively woken up due to this wakeup.
   */
  sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
                              unsigned int nb_threads,
                              sos_ret_t wakeup_status);
  #define SOS_KWQ_WAKEUP_ALL (~((unsigned int)0))
  
  
  #endif /* _SOS_KWAITQ_H_ */
  
 

/tmp/sos-code-article6/sos/main.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/main.c (2005-03-02 17:22:09.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The SOS Team /* Copyright (C) 2004  The SOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 29 
Line 28 
 #include <hwcore/paging.h> #include <hwcore/paging.h>
 #include <sos/kmem_vmm.h> #include <sos/kmem_vmm.h>
 #include <sos/kmalloc.h> #include <sos/kmalloc.h>
  #include <sos/time.h>
  #include <sos/thread.h>
 #include <sos/klibc.h> #include <sos/klibc.h>
 #include <sos/assert.h> #include <sos/assert.h>
 #include <drivers/x86_videomem.h> #include <drivers/x86_videomem.h>
Line 57 
Line 58 
  
  
 /* Clock IRQ handler */ /* Clock IRQ handler */
 static void clk_it(int intid, static void clk_it(int intid)
                    const struct sos_cpu_kstate *cpu_kstate) 
   static sos_ui32_t clock_count = 0;   static sos_ui32_t clock_count = 0;
  
Line 66 
Line 66 
                SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE,                SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE,
                clock_count);                clock_count);
   clock_count++;   clock_count++;
  
    /* Execute the expired timeout actions (if any) */
    sos_time_do_tick();
 } }
  
  
Line 74 
Line 77 
  */  */
  
 /* Helper function to dump a backtrace on bochs and/or the console */ /* Helper function to dump a backtrace on bochs and/or the console */
 static void dump_backtrace(const struct sos_cpu_kstate *cpu_kstate, static void dump_backtrace(const struct sos_cpu_state *cpu_state,
                            sos_size_t  stack_size,                            sos_size_t  stack_size,
                            sos_bool_t on_console,                            sos_bool_t on_console,
Line 127 
Line 130 
              
     }     }
  
   sos_backtrace(cpu_kstate, 15, stack_bottom, stack_size, backtracer, NULL);   sos_backtrace(cpu_state, 15, stack_bottom, stack_size, backtracer, NULL);
  
  
 /* Page fault exception handler with demand paging for the kernel */ /* 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, const struct sos_cpu_state *ctxt)
   static sos_ui32_t demand_paging_count = 0;   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);
  
   /* Check if address is covered by any VMM range */   /* Check if address is covered by any VMM range */
Line 147 
Line 150 
                      bootstrap_stack_bottom,                      bootstrap_stack_bottom,
                      bootstrap_stack_size,                      bootstrap_stack_size,
                      TRUE, TRUE);                      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)sos_cpu_kstate_get_EX_info(ctxt));                               (unsigned)sos_cpu_context_get_EX_info(ctxt));
     }     }
  
Line 180 
Line 184 
 } }
  
  
  
 /* ====================================================================== 
  * Demonstrate the use of the CPU kernet context management API: 
  *  - A coroutine prints "Hlowrd" and switches to the other after each 
  *    letter 
  *  - A coroutine prints "el ol\n" and switches back to the other after 
  *    each letter. 
  * The first to reach the '\n' returns back to main. 
  */ 
 struct sos_cpu_kstate *ctxt_hello1; 
 struct sos_cpu_kstate *ctxt_hello2; 
 struct sos_cpu_kstate *ctxt_main; 
 sos_vaddr_t hello1_stack, hello2_stack; 
  
 static void reclaim_stack(sos_vaddr_t stack_vaddr) 
 { 
   sos_kfree(stack_vaddr); 
 } 
  
  
 static void exit_hello12(sos_vaddr_t stack_vaddr) 
 { 
   sos_cpu_kstate_exit_to(ctxt_main, 
                          (sos_cpu_kstate_function_arg1_t*) reclaim_stack, 
                          stack_vaddr); 
 } 
  
  
 static void hello1 (char *str) 
 { 
   for ( ; *str != '\n' ; str++) 
     { 
       sos_bochs_printf("hello1: %c\n", *str); 
       sos_cpu_kstate_switch(& ctxt_hello1, ctxt_hello2); 
     } 
  
   /* You can uncomment this in case you explicitly want to exit 
      now. But returning from the function will do the same */ 
   /* sos_cpu_kstate_exit_to(ctxt_main, 
                          (sos_cpu_kstate_function_arg1_t*) reclaim_stack, 
                          hello1_stack); */ 
 } 
  
  
 static void hello2 (char *str) 
 { 
   for ( ; *str != '\n' ; str++) 
     { 
       sos_bochs_printf("hello2: %c\n", *str); 
       sos_cpu_kstate_switch(& ctxt_hello2, ctxt_hello1); 
     } 
  
   /* You can uncomment this in case you explicitly want to exit 
      now. But returning from the function will do the same */ 
   /* sos_cpu_kstate_exit_to(ctxt_main, 
                          (sos_cpu_kstate_function_arg1_t*) reclaim_stack, 
                          hello2_stack); */ 
 } 
  
  
 void print_hello_world () 
 { 
 #define DEMO_STACK_SIZE 1024 
   /* Allocate the stacks */ 
   hello1_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); 
   hello2_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); 
  
   /* Initialize the coroutines' contexts */ 
   sos_cpu_kstate_init(&ctxt_hello1, 
                       (sos_cpu_kstate_function_arg1_t*) hello1, 
                       (sos_ui32_t) "Hlowrd", 
                       (sos_vaddr_t) hello1_stack, DEMO_STACK_SIZE, 
                       (sos_cpu_kstate_function_arg1_t*) exit_hello12, 
                       (sos_ui32_t) hello1_stack); 
   sos_cpu_kstate_init(&ctxt_hello2, 
                       (sos_cpu_kstate_function_arg1_t*) hello2, 
                       (sos_ui32_t) "el ol\n", 
                       (sos_vaddr_t) hello2_stack, DEMO_STACK_SIZE, 
                       (sos_cpu_kstate_function_arg1_t*) exit_hello12, 
                       (sos_ui32_t) hello2_stack); 
  
   /* Go to first coroutine */ 
   sos_bochs_printf("Printing Hello World\\n...\n"); 
   sos_cpu_kstate_switch(& ctxt_main, ctxt_hello1); 
  
   /* The first coroutine to reach the '\n' switched back to us */ 
   sos_bochs_printf("Back in main !\n"); 
 } 
  
  
 /* ====================================================================== 
  * Generate page faults on an unmapped but allocated kernel virtual 
  * region, which results in a series of physical memory mappings for the 
  * faulted pages. 
  */ 
 static void test_demand_paging(int nb_alloc_vpages, int nb_alloc_ppages) 
 { 
   int i; 
   sos_vaddr_t base_vaddr; 
  
   sos_x86_videomem_printf(10, 0, 
                           SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, 
                           "Demand paging test (alloc %dMB of VMM, test %dkB RAM)", 
                           nb_alloc_vpages >> 8, nb_alloc_ppages << 2); 
    
   /* Allocate virtual memory */ 
   base_vaddr = sos_kmem_vmm_alloc(nb_alloc_vpages, 0); 
  
   SOS_ASSERT_FATAL(base_vaddr != (sos_vaddr_t)NULL); 
   sos_x86_videomem_printf(11, 0, 
                           SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                           "Allocated virtual region [0x%x, 0x%x[", 
                           base_vaddr, 
                           base_vaddr + nb_alloc_vpages*SOS_PAGE_SIZE); 
  
   /* Now use part of it in physical memory */ 
   for (i = 0 ; (i < nb_alloc_ppages) && (i < nb_alloc_vpages) ; i++) 
     { 
       /* Compute an address inside the range */ 
       sos_ui32_t *value, j; 
       sos_vaddr_t vaddr = base_vaddr; 
       vaddr += (nb_alloc_vpages - (i + 1))*SOS_PAGE_SIZE; 
       vaddr += 2345; 
  
       sos_x86_videomem_printf(12, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Writing %d at virtual address 0x%x...", 
                               i, vaddr); 
  
       /* Write at this address */ 
       value = (sos_ui32_t*)vaddr; 
       *value = i; 
  
       /* Yep ! A new page should normally have been allocated for us */ 
       sos_x86_videomem_printf(13, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Value read at address 0x%x = %d", 
                               vaddr, (unsigned)*value); 
     } 
  
   SOS_ASSERT_FATAL(SOS_OK == sos_kmem_vmm_free(base_vaddr)); 
   /* Yep ! A new page should normally have been allocated for us */ 
   sos_x86_videomem_printf(14, 0, 
                           SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                           "Done (area un-allocated)"); 
 } 
  
  
  
  * Shows how the backtrace stuff works  * Demonstrate the use of SOS kernel threads
   *  - Kernel Threads are created with various priorities and their
   *    state is printed on both the console and the bochs' 0xe9 port
   *  - For tests regarding threads' synchronization, see mouse_sim.c
  
 /* Recursive function. Print the backtrace from the innermost function */ struct thr_arg
 static void test_backtrace(int i, int magic, sos_vaddr_t stack_bottom, 
                            sos_size_t  stack_size) 
   if (i <= 0)   char character;
     {   int  color;
       /* The page fault exception handler will print the backtrace of 
          this function, because address 0x42 is not mapped */ 
       *((char*)0x42) = 12; 
       /* More direct variant: */   int col;
       /* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */   int row;
     } };
   else 
     test_backtrace(i-1, magic, stack_bottom, stack_size); 
 } 
  
  
 /* ====================================================================== 
  * Parsing of Mathematical expressions 
  * 
  * This is a recursive lexer/parser/evaluator for arithmetical 
  * expressions. Supports both binary +/-* and unary +- operators, as 
  * well as parentheses. 
  * 
  * Terminal tokens (Lexer): 
  *  - Number: positive integer number 
  *  - Variable: ascii name (regexp: [a-zA-Z]+) 
  *  - Operator: +*-/ 
  *  - Opening/closing parentheses 
  * 
  * Grammar (Parser): 
  *  Expression ::= Term E' 
  *  Expr_lr    ::= + Term Expr_lr | - Term Expr_lr | Nothing 
  *  Term       ::= Factor Term_lr 
  *  Term_lr    ::= * Factor Term_lr | / Factor Term_lr | Nothing 
  *  Factor     ::= - Factor | + Factor | Scalar | ( Expression ) 
  *  Scalar     ::= Number | Variable 
  * 
  * Note. This is the left-recursive equivalent of the following basic grammar: 
  *  Expression ::= Expression + Term | Expression - Term 
  *  Term       ::= Term * Factor | Term / Factor 
  *  factor     ::= - Factor | + Factor | Scalar | Variable | ( Expression ) 
  *  Scalar     ::= Number | Variable 
  * 
  * The parsing is composed of a 3 stages pipeline: 
  *  - The reader: reads a string 1 character at a time, transferring 
  *    the control back to lexer after each char. This function shows the 
  *    interest in using coroutines, because its state (str) is 
  *    implicitely stored in the stack between each iteration. 
  *  - The lexer: consumes the characters from the reader and identifies 
  *    the terminal tokens, 1 token at a time, transferring control back 
  *    to the parser after each token. This function shows the interest 
  *    in using coroutines, because its state (c and got_what_before) is 
  *    implicitely stored in the stack between each iteration. 
  *  - The parser: consumes the tokens from the lexer and builds the 
  *    syntax tree of the expression. There is no real algorithmic 
  *    interest in defining a coroutine devoted to do this. HOWEVER, we 
  *    do use one for that because this allows us to switch to a much 
  *    deeper stack. Actually, the parser is highly recursive, so that 
  *    the default 16kB stack of the sos_main() function might not be 
  *    enough. Here, we switch to a 64kB stack, which is safer for 
  *    recursive functions. The Parser uses intermediary functions: these 
  *    are defined and implemented as internal nested functions. This is 
  *    just for the sake of clarity, and is absolutely not mandatory for 
  *    the algorithm: one can transfer these functions out of the parser 
  *    function without restriction. 
  * 
  * The evaluator is another recursive function that reuses the 
  * parser's stack to evaluate the parsed expression with the given 
  * values for the variables present in the expression. As for the 
  * parser function, this function defines and uses a nested function, 
  * which can be extracted from the main evaluation function at will. 
  * 
  * All these functions support a kind of "exception" feature: when 
  * something goes wrong, control is transferred DIRECTLY back to the 
  * sos_main() context, without unrolling the recursions. This shows 
  * how exceptions basically work, but one should not consider this as 
  * a reference exceptions implementation. Real exception mechanisms 
  * (such as that in the C++ language) call the destructors to the 
  * objects allocated on the stack during the "stack unwinding" process 
  * upon exception handling, which complicates a lot the mechanism. We 
  * don't have real Objects here (in the OOP sense, full-featured with 
  * destructors), so we don't have to complicate things. 
  * 
  * After this little coroutine demo, one should forget all about such 
  * a low-level manual direct manipulation of stacks. This would 
  * probably mess up the whole kernel to do what we do here (locked 
  * resources such as mutex/semaphore won't be correctly unlocked, 
  * ...). Higher level "kernel thread" primitives will soon be 
  * presented, which provide a higher-level set of APIs to manage CPU 
  * contexts. You'll have to use EXCLUSIVELY those APIs. If you still 
  * need a huge stack to do recursion for example, please don't even 
  * think of changing manually the stack for something bigger ! Simply 
  * rethink your algorithm, making it non-recursive. 
  */ 
  
  
 /* The stacks involved */ 
 static char stack_reader[1024]; 
 static char stack_lexer[1024]; 
 static char deep_stack[65536]; /* For the parser and the evaluator */ 
  
 /* The CPU states for the various coroutines */ 
 static struct sos_cpu_kstate *st_reader, *st_lexer, *st_parser, 
   *st_eval, *st_free, *st_main; 
  
 /* static void demo_thread(void *arg)
  * Default exit/reclaim functions: return control to the "sos_main()" 
  * context 
  */ 
 static void reclaim(int unused) 
 { 
 } 
 static void func_exit(sos_ui32_t unused) 
   sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0);   struct thr_arg *thr_arg = (struct thr_arg*)arg;
 }   int progress = 0;
  
 /*   sos_bochs_printf("start %c", thr_arg->character);
  * The reader coroutine and associated variable. This coroutine could   while (1)
  * have been a normal function, except that the current parsed 
  * character would have to be stored somewhere. 
  */ 
 static char data_reader_to_lexer; 
  
 static void func_reader(const char *str) 
 { 
   for ( ; str && (*str != '\0') ; str++) 
       data_reader_to_lexer = *str;       progress ++;
       sos_cpu_kstate_switch(& st_reader, st_lexer);       display_bits(thr_arg->row, thr_arg->col+1, thr_arg->color, progress);
     } 
  
   data_reader_to_lexer = '\0'; 
   sos_cpu_kstate_switch(& st_reader, st_lexer); 
 } 
        sos_bochs_putchar(thr_arg->character);
  
 /*       /* Yield the CPU to another thread sometimes... */
  * The Lexer coroutine and associated types/variables. This coroutine       if ((random() % 100) == 0)
  * could have been a normal function, except that the current parsed 
  * character, token and previous token would have to be stored 
  * somewhere. 
  */ 
 #define STR_VAR_MAXLEN 16 
 static struct lex_elem 
 { 
   enum { LEX_IS_NUMBER, LEX_IS_OPER, LEX_IS_VAR, 
          LEX_IS_OPENPAR, LEX_IS_CLOSEPAR, LEX_END } type; 
   union { 
     int  number; 
     char operator; 
     char var[STR_VAR_MAXLEN]; 
   }; 
 } data_lexer_to_parser; 
  
 static void func_lexer(sos_ui32_t unused) 
 { 
   char c; 
   enum { GOT_SPACE, GOT_NUM, GOT_OP, GOT_STR, 
          GOT_OPENPAR, GOT_CLOSEPAR } got_what, got_what_before; 
  
   data_lexer_to_parser.number = 0; 
   got_what_before = GOT_SPACE; 
   do 
     { 
       /* Consume one character from the reader */ 
       sos_cpu_kstate_switch(& st_lexer, st_reader); 
       c = data_reader_to_lexer; 
  
       /* Classify the consumed character */ 
       if ( (c >= '0') && (c <= '9') ) 
         got_what = GOT_NUM; 
       else if ( (c == '+') || (c == '-') || (c == '*') || (c == '/') ) 
         got_what = GOT_OP; 
       else if ( ( (c >= 'a') && (c <= 'z') ) 
                 || ( (c >= 'A') && (c <= 'Z') ) ) 
         got_what = GOT_STR; 
       else if (c == '(') 
         got_what = GOT_OPENPAR; 
       else if (c == ')') 
         got_what = GOT_CLOSEPAR; 
       else 
         got_what = GOT_SPACE; 
  
       /* Determine whether the current token is ended */ 
       if ( (got_what != got_what_before) 
            || (got_what_before == GOT_OP) 
            || (got_what_before == GOT_OPENPAR) 
            || (got_what_before == GOT_CLOSEPAR) ) 
           /* return control back to the parser if the previous token           sos_bochs_printf("yield(%c)\n", thr_arg->character);
              has been recognized */           sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'Y');
           if ( (got_what_before != GOT_SPACE) )           SOS_ASSERT_FATAL(SOS_OK == sos_thread_yield());
             sos_cpu_kstate_switch(& st_lexer, st_parser);           sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R');
  
           data_lexer_to_parser.number = 0; 
  
       /* Update the token being currently recognized */       /* Go to sleep some other times... */
       if (got_what == GOT_OP)       else if ((random() % 200) == 0)
           data_lexer_to_parser.type = LEX_IS_OPER;           struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 };
           data_lexer_to_parser.operator = c;           sos_bochs_printf("sleep1(%c)\n", thr_arg->character);
            sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's');
            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');
       else if (got_what == GOT_NUM) 
         {       /* Go to sleep for a longer time some other times... */
           data_lexer_to_parser.type = LEX_IS_NUMBER;       else if ((random() % 300) == 0)
           data_lexer_to_parser.number *= 10; 
           data_lexer_to_parser.number += (c - '0'); 
         } 
       else if (got_what == GOT_STR) 
           char to_cat[] = { c, '\0' };           struct sos_time t = (struct sos_time){ .sec=0, .nanosec=300000000 };
           data_lexer_to_parser.type = LEX_IS_VAR;           sos_bochs_printf("sleep2(%c)\n", thr_arg->character);
           strzcat(data_lexer_to_parser.var, to_cat, STR_VAR_MAXLEN);           sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'S');
            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');
       else if (got_what == GOT_OPENPAR) 
         data_lexer_to_parser.type = LEX_IS_OPENPAR; 
       else if (got_what == GOT_CLOSEPAR) 
         data_lexer_to_parser.type = LEX_IS_CLOSEPAR; 
       got_what_before = got_what;       /* Infinite loop otherwise */
   while (c != '\0'); 
  
   /* Transfer last recognized token to the parser */ 
   if ( (got_what_before != GOT_SPACE) ) 
     sos_cpu_kstate_switch(& st_lexer, st_parser); 
  
   /* Signal that no more token are available */ 
   data_lexer_to_parser.type = LEX_END; 
   sos_cpu_kstate_switch(& st_lexer, st_parser); 
  
   /* Exception: parser asks for a token AFTER having received the last 
      one */ 
   sos_bochs_printf("Error: end of string already reached !\n"); 
   sos_cpu_kstate_switch(& st_lexer, st_main); 
  
  
 /* static void test_thread()
  * The Parser coroutine and associated types/variables 
  */ 
 struct syntax_node 
   enum { YY_IS_BINOP, YY_IS_UNAROP, YY_IS_NUM, YY_IS_VAR } type;   /* "static" variables because we want them to remain even when the
   union      function returns */
   {   static struct thr_arg arg_b, arg_c, arg_d, arg_e, arg_R, arg_S;
     int  number;   sos_ui32_t flags;
     char var[STR_VAR_MAXLEN]; 
     struct 
     { 
       char op; 
       struct syntax_node *parm_left, *parm_right; 
     } binop; 
     struct 
     { 
       char op; 
       struct syntax_node *parm; 
     } unarop; 
   }; 
 }; 
 static void func_parser(struct syntax_node ** syntax_tree)   sos_disable_IRQs(flags);
 { 
   static struct syntax_node *alloc_node_num(int val); 
   static struct syntax_node *alloc_node_var(const char * name); 
   static struct syntax_node *alloc_node_binop(char op, 
                                               struct syntax_node *parm_left, 
                                               struct syntax_node *parm_right); 
   static struct syntax_node *alloc_node_unarop(char op, 
                                                struct syntax_node *parm); 
   static struct syntax_node * get_expr(); 
   static struct syntax_node * get_expr_lr(struct syntax_node *n); 
   static struct syntax_node * get_term(); 
   static struct syntax_node * get_term_lr(struct syntax_node *n); 
   static struct syntax_node * get_factor(); 
   static struct syntax_node * get_scalar(); 
   /* Create a new node to store a number */   arg_b = (struct thr_arg) { .character='b', .col=0, .row=21, .color=0x14 };
   static struct syntax_node *alloc_node_num(int val)   sos_create_kernel_thread("YO[b]", demo_thread, (void*)&arg_b);
     { 
       struct syntax_node *n 
         = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); 
       n->type   = YY_IS_NUM; 
       n->number = val; 
       return n; 
     } 
   /* Create a new node to store a variable */ 
   static struct syntax_node *alloc_node_var(const char * name) 
     { 
       struct syntax_node *n 
         = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); 
       n->type   = YY_IS_VAR; 
       strzcpy(n->var, name, STR_VAR_MAXLEN); 
       return n; 
     } 
   /* Create a new node to store a binary operator */ 
   static struct syntax_node *alloc_node_binop(char op, 
                                               struct syntax_node *parm_left, 
                                               struct syntax_node *parm_right) 
     { 
       struct syntax_node *n 
         = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); 
       n->type             = YY_IS_BINOP; 
       n->binop.op         = op; 
       n->binop.parm_left  = parm_left; 
       n->binop.parm_right = parm_right; 
       return n; 
     } 
   /* Create a new node to store a unary operator */ 
   static struct syntax_node *alloc_node_unarop(char op, 
                                                struct syntax_node *parm) 
     { 
       struct syntax_node *n 
         = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); 
       n->type        = YY_IS_UNAROP; 
       n->unarop.op   = op; 
       n->unarop.parm = parm; 
       return n; 
     } 
   /* Raise an exception: transfer control back to main context,   arg_c = (struct thr_arg) { .character='c', .col=46, .row=21, .color=0x14 };
      without unrolling the whole recursion */   sos_create_kernel_thread("YO[c]", demo_thread, (void*)&arg_c);
   static void parser_exception(const char *str) 
     { 
       sos_bochs_printf("Parser exception: %s\n", str); 
       sos_cpu_kstate_switch(& st_parser, st_main); 
     } 
   /* Consume the current terminal "number" token and ask for a new   arg_d = (struct thr_arg) { .character='d', .col=0, .row=20, .color=0x14 };
      token */   sos_create_kernel_thread("YO[d]", demo_thread, (void*)&arg_d);
   static int get_number() 
     { 
       int v; 
       if (data_lexer_to_parser.type != LEX_IS_NUMBER) 
         parser_exception("Expected number"); 
       v = data_lexer_to_parser.number; 
       sos_cpu_kstate_switch(& st_parser, st_lexer); 
       return v; 
     } 
   /* Consume the current terminal "variable" token and ask for a new 
      token */ 
   static void get_str(char name[STR_VAR_MAXLEN]) 
     { 
       if (data_lexer_to_parser.type != LEX_IS_VAR) 
         parser_exception("Expected variable"); 
       strzcpy(name, data_lexer_to_parser.var, STR_VAR_MAXLEN); 
       sos_cpu_kstate_switch(& st_parser, st_lexer); 
     } 
   /* Consume the current terminal "operator" token and ask for a new 
      token */ 
   static char get_op() 
     { 
       char op; 
       if (data_lexer_to_parser.type != LEX_IS_OPER) 
         parser_exception("Expected operator"); 
       op = data_lexer_to_parser.operator; 
       sos_cpu_kstate_switch(& st_parser, st_lexer); 
       return op; 
     } 
   /* Consume the current terminal "parenthese" token and ask for a new 
      token */ 
   static void get_par() 
     { 
       if ( (data_lexer_to_parser.type != LEX_IS_OPENPAR) 
            && (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) ) 
         parser_exception("Expected parenthese"); 
       sos_cpu_kstate_switch(& st_parser, st_lexer); 
     } 
  
   /* Parse an Expression */ 
   static struct syntax_node * get_expr() 
     { 
       struct syntax_node *t = get_term(); 
       return get_expr_lr(t); 
     } 
   /* Parse an Expr_lr */ 
   static struct syntax_node * get_expr_lr(struct syntax_node *n) 
     { 
       if ( (data_lexer_to_parser.type == LEX_IS_OPER) 
            && ( (data_lexer_to_parser.operator == '+') 
                 || (data_lexer_to_parser.operator == '-') ) ) 
         { 
           char op = get_op(); 
           struct syntax_node *term = get_term(); 
           struct syntax_node *node_op = alloc_node_binop(op, n, term); 
           return get_expr_lr(node_op); 
         } 
       return n; 
     } 
   /* Parse a Term */ 
   static struct syntax_node * get_term() 
     { 
       struct syntax_node *f1 = get_factor(); 
       return get_term_lr(f1); 
     } 
   /* Parse a Term_lr */ 
   static struct syntax_node * get_term_lr(struct syntax_node *n) 
     { 
       if ( (data_lexer_to_parser.type == LEX_IS_OPER) 
            && ( (data_lexer_to_parser.operator == '*') 
                 || (data_lexer_to_parser.operator == '/') ) ) 
         { 
           char op = get_op(); 
           struct syntax_node *factor = get_factor(); 
           struct syntax_node *node_op = alloc_node_binop(op, n, factor); 
           return get_term_lr(node_op); 
         } 
       return n; 
     } 
   /* Parse a Factor */ 
   static struct syntax_node * get_factor() 
     { 
       if ( (data_lexer_to_parser.type == LEX_IS_OPER) 
            && ( (data_lexer_to_parser.operator == '-') 
                 || (data_lexer_to_parser.operator == '+') ) ) 
         { char op = data_lexer_to_parser.operator; 
         get_op(); return alloc_node_unarop(op, get_factor()); } 
       else if (data_lexer_to_parser.type == LEX_IS_OPENPAR) 
         { 
           struct syntax_node *expr; 
           get_par(); 
           expr = get_expr(); 
           if (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) 
             parser_exception("Mismatched parentheses"); 
           get_par(); 
           return expr; 
         } 
    
       return get_scalar(); 
     } 
   /* Parse a Scalar */ 
   static struct syntax_node * get_scalar() 
     { 
       if (data_lexer_to_parser.type != LEX_IS_NUMBER) 
         { 
           char var[STR_VAR_MAXLEN]; 
           get_str(var); 
           return alloc_node_var(var); 
         } 
       return alloc_node_num(get_number()); 
     } 
    arg_e = (struct thr_arg) { .character='e', .col=0, .row=19, .color=0x14 };
    sos_create_kernel_thread("YO[e]", demo_thread, (void*)&arg_e);
  
   /*   arg_R = (struct thr_arg) { .character='R', .col=0, .row=17, .color=0x1c };
    * Body of the function   sos_create_kernel_thread("YO[R]", demo_thread, (void*)&arg_R);
    */ 
   /* Get the first token */   arg_S = (struct thr_arg) { .character='S', .col=0, .row=16, .color=0x1c };
   sos_cpu_kstate_switch(& st_parser, st_lexer);   sos_create_kernel_thread("YO[S]", demo_thread, (void*)&arg_S);
   /* Begin the parsing ! */   sos_restore_IRQs(flags);
   *syntax_tree = get_expr(); 
   /* The result is returned in the syntax_tree parameter */ 
  
  
 /* /* ======================================================================
  * Setup the parser's pipeline  * An operating system MUST always have a ready thread ! Otherwise:
  */  * what would the CPU have to execute ?!
 static struct syntax_node * parse_expression(const char *expr) 
 { 
   struct syntax_node *retval = NULL; 
  
   /* Build the context of the functions in the pipeline */ 
   sos_cpu_kstate_init(& st_reader, 
                       (sos_cpu_kstate_function_arg1_t*)func_reader, 
                       (sos_ui32_t)expr, 
                       (sos_vaddr_t)stack_reader, sizeof(stack_reader), 
                       (sos_cpu_kstate_function_arg1_t*)func_exit, 0); 
   sos_cpu_kstate_init(& st_lexer, 
                       (sos_cpu_kstate_function_arg1_t*)func_lexer, 
                       0, 
                       (sos_vaddr_t)stack_lexer, sizeof(stack_lexer), 
                       (sos_cpu_kstate_function_arg1_t*)func_exit, 0); 
   sos_cpu_kstate_init(& st_parser, 
                       (sos_cpu_kstate_function_arg1_t*)func_parser, 
                       (sos_ui32_t) /* syntax tree ! */&retval, 
                       (sos_vaddr_t)deep_stack, sizeof(deep_stack), 
                       (sos_cpu_kstate_function_arg1_t*)func_exit, 0); 
  
   /* Parse the expression */ 
   sos_cpu_kstate_switch(& st_main, st_parser); 
   return retval; 
 } 
  
  
 /* 
  * The Evaluator coroutine and associated types/variables 
 struct func_eval_params static void idle_thread()
   const struct syntax_node *e;   sos_ui32_t idle_twiddle = 0;
   const char **var_name; 
   int *var_val; 
   int nb_vars; 
  
   int result; 
 }; 
 static void func_eval(struct func_eval_params *parms)   while (1)
 { 
   /* The internal (recursive) nested function to evaluate each node of 
      the syntax tree */ 
   static int rec_eval(const struct syntax_node *n, 
                       const char* var_name[], int var_val[], int nb_vars) 
       switch (n->type)       /* Remove this instruction if you get an "Invalid opcode" CPU
         {          exception (old 80386 CPU) */
         case YY_IS_NUM:       asm("hlt\n");
           return n->number; 
         case YY_IS_VAR:       idle_twiddle ++;
           {       display_bits(0, 0, SOS_X86_VIDEO_FG_GREEN | SOS_X86_VIDEO_BG_BLUE,
             int i;                    idle_twiddle);
             for (i = 0 ; i < nb_vars ; i++) 
               if (0 == strcmp(var_name[i], n->var)) 
                 return var_val[i]; 
  
             /* Exception: no variable with that name ! */ 
             sos_bochs_printf("ERROR: unknown variable %s\n", n->var); 
             sos_cpu_kstate_switch(& st_eval, st_main); 
           } 
  
         case YY_IS_BINOP: 
           { 
             int left = rec_eval(n->binop.parm_left, 
                                 var_name, var_val, nb_vars); 
             int right = rec_eval(n->binop.parm_right, 
                                  var_name, var_val, nb_vars); 
             switch (n->binop.op) 
               { 
               case '+': return left + right; 
               case '-': return left - right; 
               case '*': return left * right; 
               case '/': return left / right; 
               default: 
                 /* Exception: no such operator (INTERNAL error) ! */ 
                 sos_bochs_printf("ERROR: unknown binop %c\n", n->binop.op); 
                 sos_cpu_kstate_switch(& st_eval, st_main); 
               } 
           } 
  
         case YY_IS_UNAROP: 
           { 
             int arg = rec_eval(n->unarop.parm, var_name, var_val, nb_vars); 
             switch (n->unarop.op) 
               { 
               case '-': return -arg; 
               case '+': return arg; 
               default: 
                 /* Exception: no such operator (INTERNAL error) ! */ 
                 sos_bochs_printf("ERROR: unknown unarop %c\n", n->unarop.op); 
                 sos_cpu_kstate_switch(& st_eval, st_main); 
               } 
           } 
         } 
       /* Exception: no such syntax node (INTERNAL error) ! */       /* Lend the CPU to some other thread */
       sos_bochs_printf("ERROR: invalid node type\n");       sos_thread_yield();
       sos_cpu_kstate_switch(& st_eval, st_main); 
       return -1; /* let's make gcc happy */ 
  
  
   /* 
    * Function BODY 
    */ 
   /* Update p.result returned back to calling function */ 
   parms->result 
     = rec_eval(parms->e, parms->var_name, parms->var_val, parms->nb_vars); 
  
 /* 
  * Change the stack for something larger in order to call the 
  * recursive function above in a safe way 
  */ 
 static int eval_expression(const struct syntax_node *e, 
                            const char* var_name[], int var_val[], int nb_vars) 
 { 
   struct func_eval_params p 
     = (struct func_eval_params){ .e=e, 
                                  .var_name=var_name, 
                                  .var_val=var_val, 
                                  .nb_vars=nb_vars, 
                                  .result = 0 }; 
  
   sos_cpu_kstate_init(& st_eval, 
                       (sos_cpu_kstate_function_arg1_t*)func_eval, 
                       (sos_ui32_t)/* p.result is modified upon success */&p, 
                       (sos_vaddr_t)deep_stack, sizeof(deep_stack), 
                       (sos_cpu_kstate_function_arg1_t*)func_exit, 0); 
  
   /* Go ! */ 
   sos_cpu_kstate_switch(& st_main, st_eval); 
   return p.result; 
 } 
  
  
 /* 
  * Function to free the syntax tree 
  */ 
 static void func_free(struct syntax_node *n) 
 { 
   switch (n->type) 
     { 
     case YY_IS_NUM: 
     case YY_IS_VAR: 
       break; 
        
     case YY_IS_BINOP: 
       func_free(n->binop.parm_left); 
       func_free(n->binop.parm_right); 
       break; 
        
     case YY_IS_UNAROP: 
       func_free(n->unarop.parm); 
       break; 
     } 
    
   sos_kfree((sos_vaddr_t)n); 
 } 
 /* /* ======================================================================
  * Change the stack for something larger in order to call the  * The C entry point of our operating system
  * recursive function above in a safe way 
 static void free_syntax_tree(struct syntax_node *tree) 
 { 
   sos_cpu_kstate_init(& st_free, 
                       (sos_cpu_kstate_function_arg1_t*)func_free, 
                       (sos_ui32_t)tree, 
                       (sos_vaddr_t)deep_stack, sizeof(deep_stack), 
                       (sos_cpu_kstate_function_arg1_t*)func_exit, 0); 
  
   /* Go ! */ 
   sos_cpu_kstate_switch(& st_main, st_free); 
 } 
  
  
 /* The C entry point of our operating system */ 
 { {
   unsigned i;   unsigned i;
   sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr;   sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr;
   struct syntax_node *syntax_tree;   struct sos_time tick_resolution;
   /* Grub sends us a structure, called multiboot_info_t with a lot of   /* Grub sends us a structure, called multiboot_info_t with a lot of
      precious informations about the system, see the multiboot      precious informations about the system, see the multiboot
Line 1003 
Line 332 
     sos_x86_videomem_printf(1, 0,     sos_x86_videomem_printf(1, 0,
                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                             "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",                             "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",
                             "SOS", ',',                             "SOS article 6.5", ',',
                             (unsigned)mbi->mem_upper);                             (unsigned)mbi->mem_upper);
   else   else
     /* Not loaded with grub */     /* Not loaded with grub */
     sos_x86_videomem_printf(1, 0,     sos_x86_videomem_printf(1, 0,
                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                             "Welcome to SOS");                             "Welcome to SOS article 6.5");
   sos_bochs_putstring("Message in a bochs\n");   sos_bochs_putstring("Message in a bochs: This is SOS article 6.5.\n");
   /* Setup CPU segmentation and IRQ subsystem */   /* Setup CPU segmentation and IRQ subsystem */
   sos_gdt_setup();   sos_gdt_subsystem_setup();
   sos_idt_setup();   sos_idt_subsystem_setup();
   /* Setup SOS IRQs and exceptions subsystem */   /* Setup SOS IRQs and exceptions subsystem */
   sos_exceptions_setup();   sos_exception_subsystem_setup();
   sos_irq_setup();   sos_irq_subsystem_setup();
   /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */   /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */
   sos_i8254_set_frequency(100);   sos_i8254_set_frequency(100);
  
    /* Setup the kernel time subsystem to get prepared to take the timer
       ticks into account */
    tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL };
    sos_time_subsysem_setup(& tick_resolution);
  
   /* We need a multiboot-compliant boot loader to get the size of the RAM */   /* We need a multiboot-compliant boot loader to get the size of the RAM */
   if (magic != MULTIBOOT_BOOTLOADER_MAGIC)   if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
     {     {
Line 1053 
Line 387 
   /* Multiboot says: "The value returned for upper memory is maximally   /* Multiboot says: "The value returned for upper memory is maximally
      the address of the first upper memory hole minus 1 megabyte.". It      the address of the first upper memory hole minus 1 megabyte.". It
      also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */      also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */
   sos_physmem_setup((mbi->mem_upper<<10) + (1<<20),   sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20),
                     & sos_kernel_core_base_paddr,                               & sos_kernel_core_base_paddr,
                     & sos_kernel_core_top_paddr);                               & sos_kernel_core_top_paddr);
   /*   /*
    * Switch to paged-memory mode    * Switch to paged-memory mode
Line 1063 
Line 397 
  
   /* Disabling interrupts should seem more correct, but it's not really   /* Disabling interrupts should seem more correct, but it's not really
      necessary at this stage */      necessary at this stage */
   if (sos_paging_setup(sos_kernel_core_base_paddr,   SOS_ASSERT_FATAL(SOS_OK ==
                        sos_kernel_core_top_paddr))                    sos_paging_subsystem_setup(sos_kernel_core_base_paddr,
     sos_bochs_printf("Could not setup paged memory mode\n");                                               sos_kernel_core_top_paddr));
   sos_x86_videomem_printf(2, 0,   
                           SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, 
                           "Paged-memory mode is activated"); 
  
   sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT,   sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT,
                             pgflt_ex);                             pgflt_ex);
  
   /*   /*
    * Setup kernel virtual memory allocator    * Setup kernel virtual memory allocator
    */     */
   if (sos_kmem_vmm_setup(sos_kernel_core_base_paddr,   if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr,
                          sos_kernel_core_top_paddr,                                    sos_kernel_core_top_paddr,
                          bootstrap_stack_bottom,                                    bootstrap_stack_bottom,
                          bootstrap_stack_bottom + bootstrap_stack_size))                                    bootstrap_stack_bottom
                                     + bootstrap_stack_size))
  
   if (sos_kmalloc_setup())   if (sos_kmalloc_subsystem_setup())
  
   /*   /*
    * Enabling the HW interrupts here, this will make the timer HW    * Initialize the Kernel thread and scheduler subsystems
    * interrupt call our clk_it handler 
   asm volatile ("sti\n");   
    /* Initialize kernel thread subsystem */
   /*   sos_thread_subsystem_setup(bootstrap_stack_bottom,
    * Print hello world using coroutines                              bootstrap_stack_size);
    */ 
   print_hello_world(); 
  
  
   /* 
    * Run coroutine tests 
    */ 
   sos_x86_videomem_printf(4, 0, 
                           SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, 
                           "Coroutine test"); 
   sos_x86_videomem_printf(5, 0, 
                           SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                           "Parsing..."); 
   syntax_tree = parse_expression(" -  ( (69/ toto)+ ( (( - +-- 1))) + --toto*((toto+ - - y - +2*(y-toto))*y) +2*(y-toto) )/- (( y - toto)*2)"); 
  
   if (syntax_tree != NULL) 
     { 
       sos_x86_videomem_printf(6, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Evaluating..."); 
       sos_x86_videomem_printf(7, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Result=%d (if 0: check bochs output)", 
                               eval_expression(syntax_tree, 
                                               (const char*[]){"toto", "y"}, 
                                               (int[]){3, 4}, 
                                               2)); 
       free_syntax_tree(syntax_tree); 
       sos_x86_videomem_printf(8, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Done (un-allocated syntax tree)"); 
     } 
   else 
     { 
       sos_x86_videomem_printf(6, 0, 
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, 
                               "Error in parsing (see bochs output)"); 
     } 
   /*   /* Initialize the scheduler */
    * Run some demand-paging tests   sos_sched_subsystem_setup();
    */ 
   test_demand_paging(234567, 500); 
    /* Declare the IDLE thread */
    SOS_ASSERT_FATAL(sos_create_kernel_thread("idle", idle_thread, NULL) != NULL);
  
   /*   /* Enabling the HW interrupts here, this will make the timer HW
    * Create an un-resolved page fault, which will make the page fault      interrupt call the scheduler */
    * handler print the backtrace.   asm volatile ("sti\n");
    */ 
   test_backtrace(6, 0xdeadbeef, bootstrap_stack_bottom, bootstrap_stack_size); 
  
   /* 
    * System should be halted BEFORE here ! 
    */ 
  
   /* An operatig system never ends */ 
   for (;;) 
     continue; 
   return;   /* Now run some Kernel threads just for fun ! */
    extern void MouseSim();
    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_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_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_thread_exit();
    SOS_FATAL_ERROR("No trespassing !");
  
 

/tmp/sos-code-article6/sos/mouse_sim.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/mouse_sim.c (2005-03-02 17:22:08.000000000 +0100 )
(New file) 
Line 1 
  /***************************************************************************
   *   Copyright (C) 2004 by cyril dupuit                                    *
   *   cyrildupuit@hotmail.com                                               *
   *   http://perso.wanadoo.fr/koalys/                                       *
   *   (Adaptation for SOS by d2 -- 2004/12/20)                              *
   *                                                                         *
   *   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.             *
   ***************************************************************************/
  
  //*****************************************************************************
  // Nom du module : MouseSim.c
  // Description : Creation et destruction de souris mangeuse de fromages
  //*****************************************************************************
  
  #include <sos/assert.h>
  #include <sos/klibc.h>
  #include <sos/thread.h>
  #include <sos/ksynch.h>
  #include <sos/kmalloc.h>
  #include <drivers/x86_videomem.h>
   
  // Historique :
  // 20/12/04 : Suppr DestroyMap et suppr handler kbd dans version LM (d2)
  // 26/11/04 : Bug trouve et resolu dans la fonction DestroyMap
  // 21/11/04 : Creation du module V1.0
  
  //*****************************************************************************
  // Definition des equivalences :
  //*****************************************************************************
  #define MAP_X           76
  #define MAP_Y           12
  #define MAP_SIZE        MAP_X * MAP_Y
  
  #define MOUSE   0x01
  #define CHEESE  0x02
  #define OBSTACLE 0x04
  #define INPUT   0x08
  #define OUTPUT  0x10
  
  #define OBSTACLE_COUNT  100
  #define CHEESE_COUNT    650
  
  #define MOUSE_FULL      0x01
  #define MOUSE_EMPTY     0x02
  #define CHEESE_FOUND    0x04
  #define MOUSE_EXITED    0x08
  
  #define MOUSE_SPEED_MAX 1000
  #define MOUSE_SPEED_MIN 4
  
  typedef unsigned int Color_t;
  
  struct Point{
          int X;
          int Y;
          };
  
  typedef struct Point Point_t;
  
  #define Set(Reg, Flag)          Reg = (Reg | Flag)
  #define Reset(Reg, Flag)        Reg = (Reg &(~Flag))
  #define IsSet(Reg, Flag)        (Reg & Flag)
  
  
  //*****************************************************************************
  // Structure de gestion d'un element
  //*****************************************************************************
  struct Element{
          sos_ui32_t                Type;//Type d'element
          sos_ui32_t                Status;
          Color_t                        Color;//Couleur de l'element
          Point_t                        P;//Coordonnees de l'element
          struct sos_thread *        ThreadID;//Thread associe a la souris
          int                        Way;//Direction de la souris
          };
  
  typedef struct Element Element_t;
  
  //*****************************************************************************
  // 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);
  static sos_ret_t InitMapOutput(Element_t * * pMap);
  static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type);
  static void Mouse(unsigned long Param);
  static void MouseMove(Point_t * P);
  static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count);
  static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese);
  static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese);
  static sos_bool_t AffectMovement(Point_t Org, Point_t p);
  static void MouseCreator(void);
  static sos_ret_t CreateMouse(void);
  
  //*****************************************************************************
  // Variables globales de ce module :
  //*****************************************************************************
  
  static Element_t * * pMap;
  static struct sos_ksema SemMap;
  static struct sos_ksema SemMouse;
  static int MouseCount = 0;
  static int CheeseCount = 0;
  static int ObstacleCount = 0;
  static int MouseSpeed = 100;
  
  //*****************************************************************************
  // Koalys Glue
  //*****************************************************************************
  void DrawPixel(int x, int y, Color_t color)
  {
    sos_x86_videomem_putchar(y+3, x+2, color, 219);
  }
  
  
  
  //*****************************************************************************
  // Point d'entre de la 'simulation'
  //*****************************************************************************
  void MouseSim(void)
  {
    //Creation du semaphore de protection de la carte
    SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMap, "SemMap", 1));
          
    //Creation du semaphore de creation de souris
    SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMouse, "SemMouse", 2));
          
    //Creation de la carte
    SOS_ASSERT_FATAL(SOS_OK == CreateMap());
  
    //Creation du thread createur de souris
    SOS_ASSERT_FATAL(sos_create_kernel_thread("MouseCreator",
                                              (sos_kernel_thread_start_routine_t)MouseCreator,
                                              0) != NULL);
  
  }
  
  
  //*****************************************************************************
  // But de la fonction : Creer et initialiser la carte
  // Entree : Aucune
  // Parametre retourne : ERROR si la memoire est insuffisante, TRUE sinon
  //*****************************************************************************
  static sos_ret_t CreateMap(void)
  {
          pMap = (Element_t * *)sos_kmalloc(MAP_SIZE * sizeof(Element_t *), 0);
          if(pMap == NULL) return -SOS_ENOMEM;
          
          //Mettre la carte a 0
          memset(pMap, 0, MAP_SIZE * sizeof(Element_t *));
          
          //Initialisation de l'entree de la carte
          if(SOS_OK != InitMapInput(pMap))
          {//Memoire insuffisante
                  return -SOS_EFATAL;
          }
          
          //Initialisation de la sortie de la carte
          if(InitMapOutput(pMap) != SOS_OK)
          {//Memoire insuffisante
                  return -SOS_EFATAL;
          }
          
          //Initialisation du fromage
          if(ElementInit(pMap, CHEESE) != SOS_OK)
          {//Memoire insuffisante
                  return -SOS_EFATAL;
          }
          
          //Initialisation des obstacles
          if(ElementInit(pMap, OBSTACLE) != SOS_OK)
          {//Memoire insuffisante
                  return -SOS_EFATAL;
          }
          
          DrawMap();//Afficher la carte creee
          
          return SOS_OK;
  }
  
  //*****************************************************************************
  // But de la procedure : Dessiner la carte a l'ecran
  // Entree : Aucune
  // Sortie : Aucune
  //*****************************************************************************
  static void DrawMap(void)
  {
          unsigned int I;
          
          for(I = 0; I < MAP_SIZE; I++)
          {
                  if(pMap[I] != NULL)
                  {
                          DrawPixel(I % MAP_X, I/MAP_X, pMap[I]->Color);
                  }
                  else DrawPixel(I % MAP_X, I/MAP_X, SOS_X86_VIDEO_FG_BLACK);
          }
          sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                                  "Souris = %d; Fromages = %d; Obstacles = %d       ",
                                  MouseCount, CheeseCount, ObstacleCount);
  }
  
  //*****************************************************************************
  // But de la fonction : Initialiser l'entree de la carte
  // Entree :
  //      pMap : Pointeur sur la carte
  // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon
  //*****************************************************************************
  static sos_ret_t InitMapInput(Element_t * * pMap)
  {
          Element_t * pElement;
          
          //Definir le point d'entree
          pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0);
          if(pElement == NULL) return -SOS_ENOMEM;
          
          //Initialiser l'entree
          pElement->Type = INPUT;
          pElement->Status = 0;
          pElement->Color = SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE;
          pElement->P.X = 0;
          pElement->P.Y = MAP_Y / 2;
          pElement->ThreadID = 0;
  
          pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement;
  
          return SOS_OK;
  }
  
  //*****************************************************************************
  // But de la fonction : Initialiser la sortie de la carte
  // Entree :
  //      pMap : Pointeur sur la carte
  // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon
  //*****************************************************************************
  static sos_ret_t InitMapOutput(Element_t * * pMap)
  {
          Element_t * pElement;
          
          //Definir le point de sortie
          pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0);
          if(pElement == NULL) return -SOS_ENOMEM;
          
          //Initialiser l'entree
          pElement->Type = OUTPUT;
          pElement->Status = 0;
          pElement->Color = SOS_X86_VIDEO_FG_LTBLUE;
          pElement->P.X = MAP_X - 1;
          pElement->P.Y = MAP_Y / 2;
          pElement->ThreadID = 0;
  
          pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement;
  
          return SOS_OK;
  }
  
  //*****************************************************************************
  // But de la fonction : Initialiser un type d'objet sur la carte
  // Entree : 
  //      pMap : Pointeur sur la carte
  //      Type : Type d'objet a initialiser
  // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon
  //*****************************************************************************
  static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type)
  {
          unsigned int I, J;
          unsigned int Max;
          Color_t Color;
          
          if(Type == CHEESE)
          {//Type d'element = fromage
                  Max = CHEESE_COUNT;
                  Color = SOS_X86_VIDEO_FG_YELLOW;
          }
          else if(Type == OBSTACLE)
          {//Type d'element = Obstacle
                  Max = OBSTACLE_COUNT;
                  Color = SOS_X86_VIDEO_FG_GREEN;
          }
          else
          {//Aucune autre type reconnu
                  return -SOS_EINVAL;
          }
          
          for(I = 0; I < Max; I++)
          {//Tirer les fromages
                  J = random();
                  J += random();
                  J %= MAP_SIZE;
                  if(pMap[J] == NULL)
                  {//Si l'emplacement est libre
                          pMap[J] = (Element_t *)sos_kmalloc(sizeof(Element_t),
                                                             0);
                          if(pMap[J] == NULL) return -SOS_ENOMEM;
  
                          pMap[J]->Type = Type;
                          //Initialiser l'element
                          if(Type == CHEESE)
                          {//Type d'element = fromage
                                  CheeseCount++;
                          }
                          else if(Type == OBSTACLE)
                          {//Type d'element = Obstacle
                                  ObstacleCount++;
                          }
                          
                          pMap[J]->Color = Color;
                          pMap[J]->Status = 0;
                          pMap[J]->Color = Color;
                          pMap[J]->P.X = J % MAP_X;
                          pMap[J]->P.Y = J / MAP_X;
                          pMap[J]->ThreadID = 0;
                  }
          }
          
          return SOS_OK;
  }
  
  
  //*****************************************************************************
  // But du thread : Deplacer la souris sur la carte selon les regles etablies.
  // Regles :
  // - La souris doit se placer devant l'entree puis commence a recolter du
  // fromage.
  // - Des que la souris a ramasse un morceau de fromage, elle doit aller en
  // entree de la carte afin de deposer sa recolte.
  // - Si une souris a prouve sa recolte, une autre souris est creee.
  // - Si une souris prend la sortie, elle est eliminee.
  //*****************************************************************************
  static void Mouse(unsigned long Param)
  {
          Element_t * pMouse = (Element_t *)Param;
          Point_t P;
          
          SOS_ASSERT_FATAL(pMouse != NULL);
          
          //Position de depart de la souris
          P = pMouse->P;
          P = pMouse->P;
          
          while(1)
          {
            int delay_ms;
            struct sos_time delay;
  
            //La souris doit se deplacer
            sos_ksema_down(& SemMap, NULL);
  
            MouseMove(&P);
            
            sos_ksema_up(& SemMap);
  
            // Est-ce que la souris est sortie ?
            if (IsSet(pMouse->Status, MOUSE_EXITED))
              // Oui => on sort
              break;
  
            // Delai entre MOUSE_SPEED_MIN et MouseSpeed - 1
            delay_ms = MOUSE_SPEED_MIN + (random() % MouseSpeed);
            delay.sec     = delay_ms / 1000;
            delay.nanosec = (delay_ms % 1000) * 1000000;
            sos_thread_sleep(& delay);
          }
  
          // Libere la structure associee
          sos_kfree((sos_vaddr_t)pMouse);
  }
  
  //*****************************************************************************
  // But de la procedure : Deplacer la souris de maniere aleatoire sur la carte
  // Entrees :
  //      P : Position courante de la souris
  // Sorties :
  //      P : Position suivante de la souris
  //*****************************************************************************
  static void MouseMove(Point_t * P)
  {
          Point_t Org;
          Point_t p;
          Point_t Cheese;
          int Positions[8];
          int Count = 0;
          Element_t * pMouse;
  
          Org = *P;
          
          pMouse = pMap[Org.X + (Org.Y * MAP_X)];
          
          Count = EvaluatePositions(Org, Positions, &Cheese);
  
          if(Count == 0) return;
  
          p = Org;
  
          if(IsSet(pMouse->Status, CHEESE_FOUND))
          {//Prendre le fromage
                  Reset(pMouse->Status, CHEESE_FOUND);
                  p = Cheese;
          }
          else
          {//Choisir une position au hasard
                  p = ChoosePosition(pMouse, Positions, Count);
          }
          if(AffectMovement(Org, p) == FALSE) return;
          //Deplacer la souris
          pMap[Org.X + (Org.Y * MAP_X)] = NULL;
          pMap[p.X + (p.Y * MAP_X)] = pMouse;
          pMouse->P = p;
          //Mettre a jour l'affichage
          DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK);
          DrawPixel(p.X, p.Y, pMouse->Color);
          sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d      ", MouseCount, CheeseCount, ObstacleCount);
          //Mettre a jour les coordonnees
          *P = p;
  }
  
  //*****************************************************************************
  // But de la fonction : Choisir un mouvement
  // Entree :
  //      pMouse : Pointeur sur la souris
  //      Positions : Tableau de position possible
  //      Count :Nombre de positions valides
  // Sortie : Aucune
  // Parametre retourne : Position choisie
  //*****************************************************************************
  static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count)
  {
          int I, J;
          Point_t p;
          
          for(J = 0; J < Count; J++)
          {//Chercher dans le tableau si cette position est disponible
                  I = Positions[J];
                  if(I == pMouse->Way)
                  {//Poursuivre ce sens d'avance
                          p = pMouse->P;
                          switch(I)
                          {
                                  case 0:
                                          p.Y++;
                                          break;
                                  case 1:
                                          p.X++;
                                          p.Y++;
                                          break;
                                  case 2:
                                          p.X++;
                                          break;
                                  case 3:
                                          p.Y--;
                                          p.X++;
                                          break;
                                  case 4:
                                          p.Y--;
                                          break;
                                  case 5:
                                          p.Y--;
                                          p.X--;
                                          break;
                                  case 6:
                                          p.X--;
                                          break;
                                  case 7:
                                          p.X--;
                                          p.Y++;
                                          break;
                          }
                          return p;
                  }
          }
          
          J = random() % Count;
          I = Positions[J];
          if(((I + 4) % 8) == pMouse->Way)
          {//Eviter le sens inverse
                  J = (J + 1) % Count;
                  I = Positions[J];
          }
          
          p = pMouse->P;
          switch(I)
          {//Repere le deplacement
                  case 0:
                          p.Y++;
                          break;
                  case 1:
                          p.X++;
                          p.Y++;
                          break;
                  case 2:
                          p.X++;
                          break;
                  case 3:
                          p.Y--;
                          p.X++;
                          break;
                  case 4:
                          p.Y--;
                          break;
                  case 5:
                          p.Y--;
                          p.X--;
                          break;
                  case 6:
                          p.X--;
                          break;
                  case 7:
                          p.X--;
                          p.Y++;
                          break;
          }
          
          pMouse->Way = I;//Memoriser la direction selectionnee
          
          return p;
  }
  
  //*****************************************************************************
  // But de la fonction : Evaluer les positions possibles et les memoriser dans
  // un tableau de positions si aucun fromage n'a ete detecte. Si du fromage a
  // ete detecte, il sera selectionne en premier. La presence d'un fromage est
  // indiquee par le drapeau CHEESE_FOUND
  // Entree :
  //      Org : Position de la souris
  // Sorties :
  //      Positions : Tableau de positions valides
  //      Cheese : Position du fromage
  // Parametre retourne : Nombre de positions valides
  //*****************************************************************************
  static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese)
  {
          int I;
          int Count = 0;
          Point_t p;
          Point_t CheesePos;
          
          for(I = 0; I < 8; I++)
          {//Explorer toute les directions
                  p = Org;
                  switch(I)
                  {//Repere le deplacement
                          case 0:
                                  p.Y++;
                                  break;
                          case 1:
                                  p.X++;
                                  p.Y++;
                                  break;
                          case 2:
                                  p.X++;
                                  break;
                          case 3:
                                  p.Y--;
                                  p.X++;
                                  break;
                          case 4:
                                  p.Y--;
                                  break;
                          case 5:
                                  p.Y--;
                                  p.X--;
                                  break;
                          case 6:
                                  p.X--;
                                  break;
                          case 7:
                                  p.X--;
                                  p.Y++;
                                  break;
                  }
                  //Tester la collision
                  if(IsCollision(Org, p, &CheesePos) == FALSE)
                  {//La souris n'a rencontre aucun obstacle
                          Positions[Count] = I;
                          Count++;
                  }
          }
          
          *Cheese = CheesePos;
  
          return Count;
  }
  
  //*****************************************************************************
  // But de la fonction : Affecter un mouvement a la souris
  // Entrees :
  //      Org : Coordonnees de la souris
  //      p : Coordonnees voulu par la souris
  // Parametre retourne : TRUE si le mouvement a eu lieu, FALSE sinon
  //*****************************************************************************
  static sos_bool_t AffectMovement(Point_t Org, Point_t p)
  {
          Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)];
          Element_t * pElement;
          
          pElement = pMap[p.X + (p.Y * MAP_X)];
          
          //La place est libre
          if(pElement == NULL) return TRUE;//Autoriser le mouvement
  
          switch(pElement->Type)
          {
                  case CHEESE:
                          // Liberer l'emplacement memoire du fromage
                          sos_kfree((sos_vaddr_t)pElement);
                          pMap[p.X + (p.Y * MAP_X)] = NULL;
                          
                          //Donner le fromage a la souris
                          Set(pMouse->Status, MOUSE_FULL);
                          Reset(pMouse->Status, MOUSE_EMPTY);
                          pMouse->Color = SOS_X86_VIDEO_FG_MAGENTA;
                          CheeseCount--;
                          return TRUE;
                  case OUTPUT:
                          //Supprimer la souris
                          pMap[Org.X + (Org.Y * MAP_X)] = NULL;
                          MouseCount--;
                          DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK);
                          sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                                                   "Souris = %d; Fromages = %d; Obstacles = %d       ",
                                                   MouseCount, CheeseCount,
                                                   ObstacleCount);
                          Set(pMouse->Status, MOUSE_EXITED);
                          return FALSE;
                  default :
                          return FALSE;
          }
          
          return FALSE;
  }
  
  //*****************************************************************************
  // But de la fonction : Tester si une collision a eu lieu avec un obstacle
  // Entrees :
  //      Org : Coordonnees de la souris
  //      p : coordonnees desirees par la souris
  // Sortie :
  //      Cheese : Coordonnees du fromage
  // Parametre retourne : TRUE si une collision a eu lieu, FALSE sinon
  //*****************************************************************************
  static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese)
  {
          Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)];
          Element_t * pElement;
          
          //Tester les bordures de la map
          if((p.X < 0)||(p.Y < 0)) return TRUE;
          
          if((p.Y >= MAP_Y)||(p.X >= MAP_X)) return TRUE;
          
          pElement = pMap[p.X + (p.Y * MAP_X)];
          
          //L'element est vide
          if(pElement == NULL) return FALSE;
  
          //Si du fromage a ete trouve, stopper la recherche
          if(IsSet(pMouse->Status, CHEESE_FOUND)) return FALSE;
          
          switch(pElement->Type)
          {
                  case CHEESE:
                          if(IsSet(pMouse->Status, MOUSE_FULL)) return TRUE;
                          //Indiquer que du fromage a ete trouve
                          Set(pMouse->Status, CHEESE_FOUND);
                          //Retenir la position du fromage
                          (*Cheese).X = p.X;
                          (*Cheese).Y = p.Y;
                          break;
                  case INPUT:
                          if(IsSet(pMouse->Status, MOUSE_EMPTY)) return TRUE;
                          //Remplir les reserves de fromage
                          Set(pMouse->Status, MOUSE_EMPTY);
                          Reset(pMouse->Status, MOUSE_FULL);
                          pMouse->Color = SOS_X86_VIDEO_FG_LTRED;
                          //Autoriser la creation d'une autre souris
                          sos_ksema_up(& SemMouse);
                          return TRUE;
                  case OUTPUT:
                          break;
                  default :
                          return TRUE;
          }
          
          return FALSE;//Aucune collision
  }
  
  //*****************************************************************************
  // But du thread : Creer une souris et la placer autour de l'entree
  //*****************************************************************************
  static void MouseCreator(void)
  {       
          while(1)
          {
                  sos_ksema_down(& SemMouse, NULL);
                  sos_ksema_down(& SemMap, NULL);
                  CreateMouse();
                  sos_ksema_up(& SemMap);
          }
  }
  
  //*****************************************************************************
  // But de la fonction : Creer une souris et l'inserer dans la carte
  // Entree : Aucune
  // Parametre retourne : ERROR si memoire insuffisante, FALSE si souris non
  // cree, TRUE sinon
  //*****************************************************************************
  static sos_ret_t CreateMouse(void)
  {
          Element_t * pElement;
          unsigned int I;
  
          Point_t p;
  
          for(I = 0; I < 8; I++)
          {//Explorer tous les emplacements
                  p.X = 0;
                  p.Y = MAP_Y / 2;
                  switch(I)
                  {//Repere le deplacement
                          case 0:
                                  p.Y++;
                                  break;
                          case 1:
                                  p.X++;
                                  p.Y++;
                                  break;
                          case 2:
                                  p.X++;
                                  break;
                          case 3:
                                  p.Y--;
                                  p.X++;
                                  break;
                          case 4:
                                  p.Y--;
                                  break;
                          case 5:
                                  p.Y--;
                                  p.X--;
                                  break;
                          case 6:
                                  p.X--;
                                  break;
                          case 7:
                                  p.X--;
                                  p.Y++;
                                  break;
                  }
                  if((p.X >= 0)&&(p.Y >= 0)&&(p.X < MAP_X)&&(p.Y < MAP_Y))
                  {//L'emplacement est valide
                          pElement = pMap[p.X + (p.Y * MAP_X)];
                          if(pElement == NULL)
                          {//Creer la souris
                                  pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0);
                                  if(pElement != NULL)
                                  {//Initialiser l'entree
                                          pElement->Type = MOUSE;
                                          Set(pElement->Status, MOUSE_EMPTY);
                                          pElement->Color = SOS_X86_VIDEO_FG_LTRED;
                                          pElement->P = p;
                                          pElement->Way = 0;
                                          pElement->ThreadID
                                            = sos_create_kernel_thread("Mouse",
                                                                       (sos_kernel_thread_start_routine_t)Mouse,
                                                                       pElement);
                                          if(pElement->ThreadID == 0)
                                          {
                                                  sos_kfree((sos_vaddr_t)pElement);
                                                  pElement = NULL;
                                                  return -SOS_ENOMEM;
                                          }
                                          pMap[p.X + (p.Y * MAP_X)] = pElement;
                                          MouseCount++;
  
                                          DrawPixel(p.X, p.Y, pElement->Color);
                                          sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d       ", MouseCount, CheeseCount, ObstacleCount);
  
                                          return SOS_OK;
                                  }
                          }
                  }
          }
          return -SOS_EBUSY;
  }
  
  //*****************************************************************************
  // C'est fini !!!!
  //*****************************************************************************
  
 

/tmp/sos-code-article6/sos/physmem.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/physmem.c (2005-03-02 17:22:09.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
     Copyright (C) 2000  The KOS Team
  
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 59 
Line 60 
 /** We store the number of pages used/free */ /** We store the number of pages used/free */
 static sos_count_t physmem_total_pages, physmem_used_pages; static sos_count_t physmem_total_pages, physmem_used_pages;
  
 sos_ret_t sos_physmem_setup(sos_size_t ram_size, sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size,
                             /* out */sos_paddr_t *kernel_core_base,                                       /* out */sos_paddr_t *kernel_core_base,
                             /* out */sos_paddr_t *kernel_core_top)                                       /* out */sos_paddr_t *kernel_core_top)
   /* The iterator over the page descriptors */   /* The iterator over the page descriptors */
   struct physical_page_descr *ppage_descr;   struct physical_page_descr *ppage_descr;
  
 

/tmp/sos-code-article6/sos/physmem.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/physmem.h (2005-03-02 17:22:09.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  David Decotigny /* Copyright (C) 2004  David Decotigny
     Copyright (C) 2000  The KOS Team
  
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 72 
Line 73 
  * assumes identity mapping (ie virtual address == physical address)  * assumes identity mapping (ie virtual address == physical address)
  * will be stored here  * will be stored here
  */  */
 sos_ret_t sos_physmem_setup(sos_size_t ram_size, sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size,
                             /* out */sos_paddr_t *kernel_core_base,                                       /* out */sos_paddr_t *kernel_core_base,
                             /* out */sos_paddr_t *kernel_core_top);                                       /* out */sos_paddr_t *kernel_core_top);
 /** /**
  * Retrieve the total number of pages, and the number of free pages  * Retrieve the total number of pages, and the number of free pages
  
 

/tmp/sos-code-article6/sos/sched.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/sched.c (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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/errno.h>
  #include <sos/klibc.h>
  #include <sos/assert.h>
  #include <sos/list.h>
  
  #include "sched.h"
  
  
  /**
   * The definition of the scheduler queue. We could have used a normal
   * kwaitq here, it would have had the same properties. But, in the
   * definitive version (O(1) scheduler), the structure has to be a bit
   * more complicated. So, in order to keep the changes as small as
   * possible between this version and the definitive one, we don't use
   * kwaitq here.
   */
  static struct
  {
    unsigned int nr_threads;
    struct sos_thread *thread_list;
  } ready_queue;
  
  
  sos_ret_t sos_sched_subsystem_setup()
  {
    memset(& ready_queue, 0x0, sizeof(ready_queue));
  
    return SOS_OK;
  }
  
  
  /**
   * Helper function to add a thread in a ready queue AND to change the
   * state of the given thread to "READY".
   *
   * @param insert_at_tail TRUE to tell to add the thread at the end of
   * the ready list. Otherwise it is added at the head of it.
   */
  static sos_ret_t add_in_ready_queue(struct sos_thread *thr,
                                      sos_bool_t insert_at_tail)
  {
  
    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 */
    if (insert_at_tail)
      list_add_tail_named(ready_queue.thread_list, thr,
                          ready.rdy_prev, ready.rdy_next);
    else
      list_add_head_named(ready_queue.thread_list, thr,
                          ready.rdy_prev, ready.rdy_next);
    ready_queue.nr_threads ++;
  
    /* Ok, thread is now really ready to be (re)started */
    thr->state = SOS_THR_READY;
  
    return SOS_OK;
  }
  
  
  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_THR_READY == thr->state)
      return SOS_OK;
  
    /* Real-time thread: schedule it for the present turn */
    retval = add_in_ready_queue(thr, TRUE);
  
    return retval;
  }
  
  
  struct sos_thread * sos_reschedule(struct sos_thread *current_thread,
                                     sos_bool_t do_yield)
  {
  
    if (SOS_THR_ZOMBIE == current_thread->state)
      {
        /* Don't think of returning to this thread since it is
           terminated */
        /* Nop */
      }
    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 */
          add_in_ready_queue(current_thread, TRUE);
        else
          /* Put it at the head of the active list */
          add_in_ready_queue(current_thread, FALSE);
      }
  
    /* The next thread is that at the head of the ready list */
    if (ready_queue.nr_threads > 0)
      {
        struct sos_thread *next_thr;
  
        /* Queue is not empty: take the thread at its head */
        next_thr = list_pop_head_named(ready_queue.thread_list,
                                       ready.rdy_prev, ready.rdy_next);
        ready_queue.nr_threads --;
  
        return next_thr;
      }
  
    SOS_FATAL_ERROR("No kernel thread ready ?!");
    return NULL;
  }
  
 

/tmp/sos-code-article6/sos/sched.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/sched.h (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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_SCHED_H_
  #define _SOS_SCHED_H_
  
  
  /**
   * @file sched.h
   *
   * A basic scheduler with simple FIFO threads' ordering.
   *
   * The functions below manage CPU queues, and are NEVER responsible
   * 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 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/thread.h>
  
  
  /**
   * Initialize the scheduler
   *
   * @note: The use of this function is RESERVED
   */
  sos_ret_t sos_sched_subsystem_setup();
  
  
  /**
   * Mark the given thread as ready
   *
   * @note: The use of this function is RESERVED
   */
  sos_ret_t sos_sched_set_ready(struct sos_thread * thr);
  
  
  /**
   * 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_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_thread * sos_reschedule(struct sos_thread * current_thread,
                                      sos_bool_t do_yield);
  
  #endif /* _SOS_WAITQUEUE_H_ */
  
 

/tmp/sos-code-article6/sos/thread.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/thread.c (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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/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->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)
  {
    __label__ undo_creation;
    sos_ui32_t flags;
    struct sos_thread *new_thread;
  
    if (! start_func)
      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;
  
    /* 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;
  }
  
  
  /** 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);
    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);
  
    /* 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_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);
  
  
        /*
         * 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;
  }
  
  
  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;
  }
  
 

/tmp/sos-code-article6/sos/thread.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/thread.h (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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>
  
  /**
   * 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;
  
    /**
     * 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;
  
    /* Data specific to each state */
    union
    {
      struct
      {
        struct sos_thread     *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_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);
  
  
  /**
   * 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, 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);
  
  
  /**
   * 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);
  
  
  #endif /* _SOS_THREAD_H_ */
  
 

/tmp/sos-code-article6/sos/time.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/time.c (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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/assert.h>
  #include <sos/klibc.h>
  #include <hwcore/irq.h>
  #include <sos/list.h>
  
  #include "time.h"
  
  
  /**
   * Number of nanoseconds in 1 second
   */
  #define NS_IN_SEC 1000000000UL
  
  
  /**
   * The list of timeout actions waiting for a timeout. The timeout
   * actions are stored in the list in increasing initial timeout
   * order. Actually, the "timeout" field won't reflect this initial
   * timeout: for each element in the list, it stores the timeout
   * _difference_ between the timeout action and the previous in the
   * list.
   */
  static struct sos_timeout_action *timeout_action_list;
  
  
  /**
   * Current resolution of a time tick
   */
  static struct sos_time tick_resolution;
  
  
  /**
   * Time elapsed between boot and last timer tick
   *
   * @note No 'volatile' here because the tick value is NEVER modified
   * while in any of the functions below: it is modified only out of
   * these functions by the IRQ timer handler because these functions
   * are protected against timer IRQ and are "one shot" (no busy waiting
   * for a change in the tick's value).
   */
  static struct sos_time last_tick_time;
  
  
  sos_ret_t sos_time_inc(struct sos_time *dest,
                         const struct sos_time *to_add)
  {
    /* nanosec is always < 1e9 so that their sum is always < 2e9, which
       is smaller than 2^32-1 */
    sos_ui32_t sigma_ns = dest->nanosec + to_add->nanosec;
    
    dest->sec     += to_add->sec;
    dest->sec     += sigma_ns / NS_IN_SEC;
    dest->nanosec  = sigma_ns % NS_IN_SEC;
    return SOS_OK;
  }
  
  
  sos_ret_t sos_time_dec(struct sos_time *dest,
                         const struct sos_time *to_dec)
  {
    /* nanosec is always < 1e9 so that their difference is always in
       (-1e9, 1e9), which is compatible with the (-2^31, 2^31 - 1)
       cpacity of a signed dword */
    sos_si32_t diff_ns = ((sos_si32_t)dest->nanosec)
                          - ((sos_si32_t)to_dec->nanosec);
  
    /* Make sure substraction is possible */
    SOS_ASSERT_FATAL(dest->sec >= to_dec->sec);
    if (dest->sec == to_dec->sec)
      SOS_ASSERT_FATAL(dest->nanosec >= to_dec->nanosec);
  
    dest->sec     -= to_dec->sec;
    if (diff_ns > 0)
      dest->sec     += diff_ns / NS_IN_SEC;
    else
      dest->sec     -= ((-diff_ns) / NS_IN_SEC);
    dest->nanosec  = (NS_IN_SEC + diff_ns) % NS_IN_SEC;
    if (diff_ns < 0)
      dest->sec --;
    return SOS_OK;
  }
  
  
  int sos_time_cmp(const struct sos_time *t1,
                   const struct sos_time *t2)
  {
    /* Compare seconds */
    if (t1->sec < t2->sec)
      return -1;
    else if (t1->sec > t2->sec)
      return 1;
  
    /* seconds are equal => compare nanoseconds */
    else if (t1->nanosec < t2->nanosec)
      return -1;
    else if (t1->nanosec > t2->nanosec)
      return 1;
  
    /* else: sec and nanosecs are equal */
    return 0;
  }
  
  
  sos_bool_t sos_time_is_zero(const struct sos_time *tm)
  {
    return ( (0 == tm->sec) && (0 == tm->nanosec) );
  }
  
  
  sos_ret_t sos_time_subsysem_setup(const struct sos_time *initial_resolution)
  {
    timeout_action_list = NULL;
    last_tick_time = (struct sos_time) { .sec = 0, .nanosec = 0 };
    memcpy(& tick_resolution, initial_resolution, sizeof(struct sos_time));
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_time_get_tick_resolution(struct sos_time *resolution)
  {
    sos_ui32_t flags;
    sos_disable_IRQs(flags);
  
    memcpy(resolution, & tick_resolution, sizeof(struct sos_time));
  
    sos_restore_IRQs(flags);
    return SOS_OK; 
  }
  
  
  sos_ret_t sos_time_set_tick_resolution(const struct sos_time *resolution)
  {
    sos_ui32_t flags;
  
    sos_disable_IRQs(flags);
    memcpy(& tick_resolution, resolution, sizeof(struct sos_time));
    sos_restore_IRQs(flags);
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_time_get_now(struct sos_time *now)
  {
    sos_ui32_t flags;
    sos_disable_IRQs(flags);
  
    memcpy(now, & last_tick_time, sizeof(struct sos_time));
  
    sos_restore_IRQs(flags);
    return SOS_OK;  
  }
  
  
  /**
   * Helper routine to add the action in the list. MUST be called with
   * interrupts disabled !
   */
  static sos_ret_t _add_action(struct sos_timeout_action *act,
                               const struct sos_time *due_date,
                               sos_bool_t is_relative_due_date,
                               sos_timeout_routine_t *routine,
                               void *routine_data)
  {
    struct sos_timeout_action *other, *insert_before;
    int nb_act;
  
    /* Delay must be specified */
    if (due_date == NULL)
      return -SOS_EINVAL;
  
    /* Action container MUST be specified */
    if (act == NULL)
      return -SOS_EINVAL;
  
    /* Refuse to add an empty action */
    if (NULL == routine)
      return -SOS_EINVAL;
  
    /* Refuse to add the action if it is already added */
    if (NULL != act->tmo_next)
      return -SOS_EBUSY;
  
    /* Compute action absolute due date */
    if (is_relative_due_date)
      {
        /* The provided due_date is relative to the current time */
        memcpy(& act->timeout, & last_tick_time, sizeof(struct sos_time));
        sos_time_inc(& act->timeout, due_date);
      }
    else
      {
        /* The provided due_date is absolute (ie relative to the system
           boot instant) */
  
        if (sos_time_cmp(due_date, & last_tick_time) < 0)
          /* Refuse to add a past action ! */
          return -SOS_EINVAL;
  
        memcpy(& act->timeout, due_date, sizeof(struct sos_time));
      }    
  
    /* Prepare the action data structure */
    act->routine      = routine;
    act->routine_data = routine_data;
  
    /* Find the right place in the list of the timeout action. */
    insert_before = NULL;
    list_foreach_forward_named(timeout_action_list,
                               other, nb_act,
                               tmo_prev, tmo_next)
      {
        if (sos_time_cmp(& act->timeout, & other->timeout) < 0)
          {
            insert_before = other;
            break;
          }
  
        /* Loop over to next timeout */
      }
  
    /* Now insert the action in the list */
    if (insert_before != NULL)
      list_insert_before_named(timeout_action_list, insert_before, act,
                              tmo_prev, tmo_next);
    else
      list_add_tail_named(timeout_action_list, act,
                          tmo_prev, tmo_next);
  
    return SOS_OK;  
  }
  
  
  sos_ret_t
  sos_time_register_action_relative(struct sos_timeout_action *act,
                                    const struct sos_time *delay,
                                    sos_timeout_routine_t *routine,
                                    void *routine_data)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = _add_action(act, delay, TRUE, routine, routine_data);
    sos_restore_IRQs(flags);
  
    return retval;
  }
  
  
  sos_ret_t
  sos_time_register_action_absolute(struct sos_timeout_action *act,
                                    const struct sos_time *date,
                                    sos_timeout_routine_t *routine,
                                    void *routine_data)
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
    retval = _add_action(act, date, FALSE, routine, routine_data);
    sos_restore_IRQs(flags);
  
    return retval;
  }
  
  
  /**
   * Helper routine to remove the action from the list. MUST be called
   * with interrupts disabled !
   */
  static sos_ret_t _remove_action(struct sos_timeout_action *act)
  {
    /* Don't do anything if action is not in timeout list */
    if (NULL == act->tmo_next)
      return -SOS_EINVAL;
  
    /* Update the action's remaining timeout */
    if (sos_time_cmp(& act->timeout, & last_tick_time) <= 0)
      act->timeout = (struct sos_time){ .sec=0, .nanosec=0 };
    else
      sos_time_dec(& act->timeout, & last_tick_time);
  
    /* Actually remove the action from the list */
    list_delete_named(timeout_action_list, act,
                      tmo_prev, tmo_next);
    act->tmo_prev = act->tmo_next = NULL;
  
    return SOS_OK;  
  }
  
  
  sos_ret_t sos_time_unregister_action(struct sos_timeout_action *act)
  {
    sos_ret_t retval;
    sos_ui32_t flags;
  
    sos_disable_IRQs(flags);
    retval = _remove_action(act);
    sos_restore_IRQs(flags);
  
    return SOS_OK;  
  }
  
  
  sos_ret_t sos_time_do_tick()
  {
    sos_ui32_t flags;
    
    sos_disable_IRQs(flags);
  
    /* Update kernel time */
    sos_time_inc(& last_tick_time, & tick_resolution);
  
    while (! list_is_empty_named(timeout_action_list, tmo_prev, tmo_next))
      {
        struct sos_timeout_action *act;
        act = list_get_head_named(timeout_action_list, tmo_prev, tmo_next);
  
        /* Did we go too far in the actions' list ? */
        if (sos_time_cmp(& last_tick_time, & act->timeout) < 0)
          {
            /* Yes: No need to look further. */
            break;
          }
  
        /* Remove the action from the list */
        _remove_action(act);
  
        /* Call the action's routine */
        act->routine(act);
      }
  
    sos_restore_IRQs(flags);
    return SOS_OK;
  }
  
 

/tmp/sos-code-article6/sos/time.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/time.h (2005-03-02 17:22:09.000000000 +0100 )
(New file) 
Line 1 
  /* 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_TIME_H_
  #define _SOS_TIME_H_
  
  /**
   * @file time.h
   *
   * Primitives and callbacks related to kernel time management (timer
   * IRQ)
   */
  
  #include <sos/types.h>
  #include <sos/errno.h>
  #include <sos/klibc.h>
  
  
  /* =======================================================================
   * Library of time manipulation functions
   */
  struct sos_time
  {
    sos_ui32_t sec;
    sos_ui32_t nanosec;
  };
  
  sos_ret_t sos_time_inc(struct sos_time *dest,
                         const struct sos_time *to_add);
  
  sos_ret_t sos_time_dec(struct sos_time *dest,
                         const struct sos_time *to_dec);
  
  int sos_time_cmp(const struct sos_time *t1,
                   const struct sos_time *t2);
  
  sos_bool_t sos_time_is_zero(const struct sos_time *tm);
  
  
  
  /* =======================================================================
   * Kernel time management. This is not the same as the "system-time",
   * ie it does not not take into account the system-time adjustments
   * (NTP, daylight saving times, etc...): this is the job of a
   * system-time subsystem.
   */
  
  
  /**
   * Initialize kernel time subsystem.
   *
   * @param initial_resolution The initial time resolution. MUST be
   * consistent with that of the hardware timer
   */
  sos_ret_t sos_time_subsysem_setup(const struct sos_time *initial_resolution);
  
  
  /**
   * Value of the interval between 2 time ticks. Should be consistent
   * with the configuration of the hardware timer.
   */
  sos_ret_t sos_time_get_tick_resolution(struct sos_time *resolution);
  
  
  /**
   * Change the value of the interval between 2 time ticks. Must be
   * called each time the hardware timer is reconfigured.
   *
   * @note MUST be consistent with that of the hardware timer
   */
  sos_ret_t sos_time_set_tick_resolution(const struct sos_time *resolution);
  
  
  /**
   * Get the time elapsed since system boot. Does not take into account
   * the system-time adjustment (NTP, daylight saving times, etc...):
   * this is the job of a system-time subsystem.
   */
  sos_ret_t sos_time_get_now(struct sos_time *now);
  
  
  
  /* =======================================================================
   * Routines to schedule future execution of routines: "timeout" actions
   */
  
  /* Forward declaration */
  struct sos_timeout_action;
  
  /**
   * Prototype of a timeout routine. Called with IRQ disabled !
   */
  typedef void (sos_timeout_routine_t)(struct sos_timeout_action *);
  
  
  /**
   * The structure of a timeout action. This structure should have been
   * opaque to the other parts of the kernel. We keep it public here so
   * that struct sos_timeout_action can be allocated on the stack from
   * other source files in the kernel. However, all the fields should be
   * considered read-only for modules others than time.{ch}.
   *
   * @note After an action has been allocated (on the stack or kmalloc),
   * it MUST be initialized with sos_time_init_action below !
   */
  struct sos_timeout_action
  {
    /** PUBLIC: Address of the timeout routine */
    sos_timeout_routine_t *routine;
  
    /** PUBLIC: (Custom) data available for this routine */
    void                  *routine_data;
    
    /** PUBLIC: 2 meanings:
     *  - before and while in the timeout list: absolute due date of the
     *    timeout action
     *  - once removed from timeout list: the time remaining in the
     *    initial timeout (might be 0 if timeout expired) at removal
     *    time
     */
    struct sos_time           timeout;
  
    /** PRIVATE: To chain the timeout actions */
    struct sos_timeout_action *tmo_prev, *tmo_next;
  };
  
  
  /**
   * Initialize a timeout action. MUST be called immediately after
   * (stack or kmalloc) allocation of the action.
   *
   * @param ptr_act Pointer to the action to initialize.
   */
  #define sos_time_init_action(ptr_act) \
    ({ (ptr_act)->tmo_prev = (ptr_act)->tmo_next = NULL; /* return */ SOS_OK; })
  
  
  /**
   * Add the given action in the timeout list, so that it will be
   * triggered after the specified delay RELATIVE to the time when the
   * function gets called. The action is always inserted in the list.
   *
   * @param act The action to be initialized by the function upon
   * insertion in the timeout list.
   *
   * @param delay Delay until the action is fired. If 0, then it is
   * fired at next timer IRQ. The action will be fired in X ticks, with
   * X integer and >= delay.
   *
   * @param routine The timeout routine to call when the timeout will be
   * triggered.
   *
   * @param routine_data The data available to the routine when it will
   * be called.
   *
   * @note 'act' MUST remain valid until it is either fired or removed
   * (with sos_time_remove_action)
   */
  sos_ret_t
  sos_time_register_action_relative(struct sos_timeout_action *act,
                                    const struct sos_time *delay,
                                    sos_timeout_routine_t *routine,
                                    void *routine_data);
  
  
  /**
   * Add the given action in the timeout list, so that it will be
   * triggered after the specified ABSOLUTE date (relative to system
   * boot time). The action is always inserted in the list.
   *
   * @param act The action to be initialized by the function upon
   * insertion in the timeout list.
   *
   * @param date Absolute date (relative to system boot time) when the
   * action will be triggered.
   *
   * @param routine The timeout routine to call when the timeout will be
   * triggered.
   *
   * @param routine_data The data available to the routine when it will
   * be called.
   *
   * @note 'act' MUST remain valid until it is either fired or removed
   * (with sos_time_remove_action)
   */
  sos_ret_t
  sos_time_register_action_absolute(struct sos_timeout_action *act,
                                    const struct sos_time *date,
                                    sos_timeout_routine_t *routine,
                                    void *routine_data);
  
  
  /**
   * The action is removed and its timeout is updated to reflect the
   * time remaining.
   */
  sos_ret_t sos_time_unregister_action(struct sos_timeout_action *act);
  
  
  /**
   * Timer IRQ callback. Call and remove expired actions from the list.
   *
   * @note The use of this function is RESERVED (to timer IRQ)
   */
  sos_ret_t sos_time_do_tick();
  
  
  #endif /* _SOS_TIME_H_ */
  
 

/tmp/sos-code-article6/sos/types.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/types.h (2005-03-02 17:22:09.000000000 +0100 )
Line 1 
Line 1 
 /* Copyright (C) 2004  The SOS Team /* Copyright (C) 2004  The SOS Team
    Copyright (C) 1999  Free Software Foundation, Inc. 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
Line 28 
Line 27 
 /** Physical address */ /** Physical address */
 typedef unsigned int       sos_paddr_t; typedef unsigned int       sos_paddr_t;
  
 /** Generic virtual address (kernel or user) */ /** Kernel virtual address */
  
 /** Memory size of an object (positive) */ /** Memory size of an object (positive) */
  
 

/tmp/sos-code-article6/support/sos.lds (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/support/sos.lds (2005-03-02 17:22:09.000000000 +0100 )
Line 79 
Line 79 
         PROVIDE(erodata = .);         PROVIDE(erodata = .);
         PROVIDE(_erodata = .);         PROVIDE(_erodata = .);
     }     }
  
     /* We take note of the end of the data to load */     /* We take note of the end of the data to load */
     __e_load = .;     __e_load = .;
  
  
 

/tmp/sos-code-article6/userland/Makefile (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/userland/Makefile (2005-03-02 17:22:10.000000000 +0100 )
(New file) 
Line 1 
  ## 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
  CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I..
  LDFLAGS = -Wl,--warn-common -nostdlib -Wl,-Tldscript.lds
  
  # Main target
  all: userprogs.kimg
  
  -include .mkvars
  
  
  PWD := $(shell pwd)
  
  # Programs generation
  $(PROGS):
          $(CC) -static $(LDFLAGS) -o $@ $^
  
  # Generation of the libC
  libc.a: libc.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 { .rodata . : { " > .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 "  _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*
  
 

/tmp/sos-code-article6/userland/ldscript.lds (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/userland/ldscript.lds (2005-03-02 17:22:10.000000000 +0100 )
(New file) 
Line 1 
  /* 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*)
          PROVIDE(erodata = .);
          PROVIDE(_erodata = .);
      }
  
      /* Beginning of the BSS section (global uninitialized data) */
      .bss SIZEOF(.rodata) + ADDR(.rodata) :
      {   *(.bss)
          *(COMMON)
          PROVIDE(ebss = .);
          PROVIDE(_ebss = .);
      }
  
      /* We don't care of the note, indent, comment, etc.. sections
         generated by gcc */
      /DISCARD/ :{
                  *(.note*)
                  *(.indent)
                  *(.comment)
                  *(.stab)
                  *(.stabstr)
      }
  }


Legend:
 
identical lines
Removed from old 
changed lines
 Added in new

File list:
    
../sos-code-article6.5/Makefile
    ../sos-code-article6.5/VERSION
    ../sos-code-article6.5/drivers/bochs.c
    ../sos-code-article6.5/drivers/bochs.h
    ../sos-code-article6.5/drivers/x86_videomem.c
    ../sos-code-article6.5/drivers/x86_videomem.h
    ../sos-code-article6.5/hwcore/cpu_context.c
    ../sos-code-article6.5/hwcore/cpu_context.h
    ../sos-code-article6.5/hwcore/cpu_context_switch.S
    ../sos-code-article6.5/hwcore/exception.c
    ../sos-code-article6.5/hwcore/exception.h
    ../sos-code-article6.5/hwcore/exception_wrappers.S
    ../sos-code-article6.5/hwcore/gdt.c
    ../sos-code-article6.5/hwcore/gdt.h
    ../sos-code-article6.5/hwcore/i8254.c
    ../sos-code-article6.5/hwcore/i8254.h
    ../sos-code-article6.5/hwcore/i8259.c
    ../sos-code-article6.5/hwcore/i8259.h
    ../sos-code-article6.5/hwcore/idt.c
    ../sos-code-article6.5/hwcore/idt.h
    ../sos-code-article6.5/hwcore/ioports.h
    ../sos-code-article6.5/hwcore/irq.c
    ../sos-code-article6.5/hwcore/irq.h
    ../sos-code-article6.5/hwcore/irq_wrappers.S
    ../sos-code-article6.5/hwcore/paging.c
    ../sos-code-article6.5/hwcore/paging.h
    ../sos-code-article6.5/hwcore/segment.h
    ../sos-code-article6.5/sos/assert.c
    ../sos-code-article6.5/sos/assert.h
    ../sos-code-article6.5/sos/errno.h
    ../sos-code-article6.5/sos/klibc.c
    ../sos-code-article6.5/sos/klibc.h
    ../sos-code-article6.5/sos/kmalloc.c
    ../sos-code-article6.5/sos/kmalloc.h
    ../sos-code-article6.5/sos/kmem_slab.c
    ../sos-code-article6.5/sos/kmem_slab.h
    ../sos-code-article6.5/sos/kmem_vmm.c
    ../sos-code-article6.5/sos/kmem_vmm.h
    ../sos-code-article6.5/sos/ksynch.c
    ../sos-code-article6.5/sos/ksynch.h
    ../sos-code-article6.5/sos/kwaitq.c
    ../sos-code-article6.5/sos/kwaitq.h
    ../sos-code-article6.5/sos/main.c
    ../sos-code-article6.5/sos/mouse_sim.c
    ../sos-code-article6.5/sos/physmem.c
    ../sos-code-article6.5/sos/physmem.h
    ../sos-code-article6.5/sos/sched.c
    ../sos-code-article6.5/sos/sched.h
    ../sos-code-article6.5/sos/thread.c
    ../sos-code-article6.5/sos/thread.h
    ../sos-code-article6.5/sos/time.c
    ../sos-code-article6.5/sos/time.h
    ../sos-code-article6.5/sos/types.h
    ../sos-code-article6.5/support/sos.lds
    ../sos-code-article6.5/userland/Makefile
    ../sos-code-article6.5/userland/ldscript.lds