/tmp/sos-code-article6/drivers/bochs.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/drivers/bochs.c (2005-01-04 04:12:14.000000000 +0100 )
Line 30 
Line 30 
   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-01-04 04:12:14.000000000 +0100 )
Line 34 
Line 34 
  
 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/hwcore/cpu_context.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/cpu_context.c (2005-01-04 04:12:15.000000000 +0100 )
Line 68 
Line 68 
 } __attribute__((packed)); } __attribute__((packed));
  
  
  
                           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,
  
 

/tmp/sos-code-article6/hwcore/exception.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/exception.c (2005-01-04 04:12:14.000000000 +0100 )
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) sos_ret_t sos_exception_subsystem_setup(void)
   /* 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
  
 

/tmp/sos-code-article6/hwcore/exception.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/exception.h (2005-01-04 04:12:15.000000000 +0100 )
Line 73 
Line 73 
 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_kstate *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);
  
 

/tmp/sos-code-article6/hwcore/gdt.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/gdt.c (2005-01-04 04:12:15.000000000 +0100 )
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;
  
  
 

/tmp/sos-code-article6/hwcore/gdt.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/gdt.h (2005-01-04 04:12:15.000000000 +0100 )
Line 36 
Line 36 
  * 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/i8259.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/i8259.c (2005-01-04 04:12:15.000000000 +0100 )
Line 24 
Line 24 
 #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-01-04 04:12:15.000000000 +0100 )
Line 32 
Line 32 
  */  */
  
 /** 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-01-04 04:12:15.000000000 +0100 )
Line 70 
Line 70 
  
 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-01-04 04:12:15.000000000 +0100 )
Line 57 
Line 57 
  
 /** 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/irq.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/irq.c (2005-01-04 04:12:15.000000000 +0100 )
Line 30 
Line 30 
 /** 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-01-04 04:12:15.000000000 +0100 )
Line 68 
Line 68 
  
  
 /** Setup the PIC */ /** Setup the PIC */
 sos_ret_t sos_irq_setup(void); sos_ret_t sos_irq_subsystem_setup(void);
  
 /** /**
Line 90 
Line 90 
  
  
 /** /**
  * 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/paging.c (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/paging.c (2005-01-04 04:12:15.000000000 +0100 )
Line 182 
Line 182 
 } }
  
  
 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;  
  
 

/tmp/sos-code-article6/hwcore/paging.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/hwcore/paging.h (2005-01-04 04:12:15.000000000 +0100 )
Line 62 
Line 62 
  * 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
  
 

/tmp/sos-code-article6/Makefile (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/Makefile (2005-01-04 04:12:14.000000000 +0100 )
Line 10 
Line 10 
           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/kthread.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
  
 

/tmp/sos-code-article6/sos/assert.h (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/sos/assert.h (2005-01-04 04:12:15.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-01-04 04:12:15.000000000 +0100 )
Line 31 
Line 31 
 #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-01-04 04:12:15.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-01-04 04:12:15.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-01-04 04:12:15.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-01-04 04:12:15.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-01-04 04:12:15.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-01-04 04:12:15.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-01-04 04:12:15.000000000 +0100 )
Line 203 
Line 203 
 } }
  
  
 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 222 
   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 307 
   /* 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-01-04 04:12:15.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-01-04 04:12:16.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_kthread_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_kthread_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_kthread_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_kthread_get_current() != mutex->owner)
      retval = -SOS_EPERM;
    else if (sos_kwaitq_is_empty(& mutex->kwaitq))
      {
        mutex->owner = NULL;
        retval = SOS_OK;
      }
    else
      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-01-04 04:12:16.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_kthread *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/kthread.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/kthread.c (2005-01-04 04:12:16.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/physmem.h>
  #include <sos/kmem_slab.h>
  #include <sos/kmalloc.h>
  #include <sos/klibc.h>
  #include <sos/list.h>
  #include <sos/assert.h>
  
  #include <hwcore/irq.h>
  
  #include "kthread.h"
  
  
  /**
   * The size of the stack of a kernel thread
   */
  #define SOS_KTHREAD_STACK_SIZE (1*SOS_PAGE_SIZE)
  
  
  /**
   * The identifier of the thread currently running on CPU.
   *
   * We only support a SINGLE processor, ie a SINGLE kernel thread
   * running at any time in the system. This greatly simplifies the
   * implementation of the system, since we don't have to complicate
   * things in order to retrieve the identifier of the threads running
   * on the CPU. On multiprocessor systems the current_kthread below is
   * an array indexed by the id of the CPU, so that the challenge is to
   * retrieve the identifier of the CPU. This is usually done based on
   * the stack address (Linux implementation) or on some form of TLS
   * ("Thread Local Storage": can be implemented by way of LDTs for the
   * processes, accessed through the fs or gs registers).
   */
  static volatile struct sos_kthread *current_kthread = NULL;
  
  
  /*
   * The list of kernel threads currently in the system.
   *
   * @note We could have used current_kthread for that...
   */
  static struct sos_kthread *kthread_list = NULL;
  
  
  /**
   * The Cache of kthread structures
   */
  static struct sos_kslab_cache *cache_kthread;
  
  
  struct sos_kthread *sos_kthread_get_current()
  {
    SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
    return (struct sos_kthread*)current_kthread;
  }
  
  
  inline static sos_ret_t _set_current(struct sos_kthread *thr)
  {
    SOS_ASSERT_FATAL(thr->state == SOS_KTHR_READY);
    current_kthread = thr;
    current_kthread->state = SOS_KTHR_RUNNING;
    return SOS_OK;
  }
  
  
  sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
                                        sos_size_t init_thread_stack_size)
  {
    struct sos_kthread *myself;
  
    /* Allocate the cache of kthreads */
    cache_kthread = sos_kmem_cache_create("kthread",
                                          sizeof(struct sos_kthread),
                                          2,
                                          0,
                                          SOS_KSLAB_CREATE_MAP
                                          | SOS_KSLAB_CREATE_ZERO);
    if (! cache_kthread)
      return -SOS_ENOMEM;
  
    /* Allocate a new kthread structure for the current running thread */
    myself = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread,
                                                        SOS_KSLAB_ALLOC_ATOMIC);
    if (! myself)
      return -SOS_ENOMEM;
  
    /* Initialize the thread attributes */
    strzcpy(myself->name, "[kinit]", SOS_KTHR_MAX_NAMELEN);
    myself->state           = SOS_KTHR_CREATED;
    myself->stack_base_addr = init_thread_stack_base_addr;
    myself->stack_size      = init_thread_stack_size;
  
    /* Do some stack poisoning on the bottom of the stack, if needed */
    sos_cpu_kstate_prepare_detect_stack_overflow(myself->cpu_kstate,
                                                 myself->stack_base_addr,
                                                 myself->stack_size);
  
    /* Add the thread in the global list */
    list_singleton_named(kthread_list, myself, gbl_prev, gbl_next);
  
    /* Ok, now pretend that the running thread is ourselves */
    myself->state = SOS_KTHR_READY;
    _set_current(myself);
  
    return SOS_OK;
  }
  
  
  struct sos_kthread *sos_kthread_create(const char *name,
                                         sos_kthread_start_routine_t start_func,
                                         void *start_arg)
  {
    __label__ undo_creation;
    struct sos_kthread *new_thread;
  
    if (! start_func)
      return NULL;
  
    /* Allocate a new kthread structure for the current running thread */
    new_thread
      = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread,
                                                   SOS_KSLAB_ALLOC_ATOMIC);
    if (! new_thread)
      return NULL;
  
    /* Initialize the thread attributes */
    strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_KTHR_MAX_NAMELEN);
    new_thread->state    = SOS_KTHR_CREATED;
  
    /* Allocate the stack for the new thread */
    new_thread->stack_base_addr = sos_kmalloc(SOS_KTHREAD_STACK_SIZE, 0);
    new_thread->stack_size      = SOS_KTHREAD_STACK_SIZE;
    if (! new_thread->stack_base_addr)
      goto undo_creation;
  
    /* Initialize the CPU context of the new thread */
    if (SOS_OK
        != sos_cpu_kstate_init(& new_thread->cpu_kstate,
                               (sos_cpu_kstate_function_arg1_t*) start_func,
                               (sos_ui32_t) start_arg,
                               new_thread->stack_base_addr,
                               new_thread->stack_size,
                               (sos_cpu_kstate_function_arg1_t*) sos_kthread_exit,
                               (sos_ui32_t) NULL))
      goto undo_creation;
  
    /* Add the thread in the global list */
    list_add_tail_named(kthread_list, new_thread, gbl_prev, gbl_next);
  
    /* Mark the thread ready */
    if (SOS_OK != sos_sched_set_ready(new_thread))
      goto undo_creation;
  
    /* Normal non-erroneous end of function */
    return new_thread;
  
   undo_creation:
    sos_kmem_cache_free((sos_vaddr_t) new_thread);
    return NULL;
  }
  
  
  /** Function called after thr has terminated. Called from inside the context
      of another thread, interrupts disabled */
  static void delete_thread(struct sos_kthread *thr)
  {
    list_delete_named(kthread_list, thr, gbl_prev, gbl_next);
  
    sos_cpu_kstate_detect_stack_overflow(thr->cpu_kstate,
                                         thr->stack_base_addr,
                                         thr->stack_size);
  
    sos_kfree((sos_vaddr_t) thr->stack_base_addr);
    memset(thr, 0x0, sizeof(struct sos_kthread));
    sos_kmem_cache_free((sos_vaddr_t) thr);
  }
  
  
  void sos_kthread_exit()
  {
    sos_ui32_t flags;
    struct sos_kthread *myself, *next_thread;
  
    myself = sos_kthread_get_current();
  
    /* Refuse to end the current executing thread if it still holds a
       resource ! */
    SOS_ASSERT_FATAL(list_is_empty_named(myself->kwaitq_list,
                                         prev_entry_for_kthread,
                                         next_entry_for_kthread));
  
    /* Prepare to run the next thread */
    sos_disable_IRQs(flags);
    myself->state = SOS_KTHR_ZOMBIE;
    next_thread = sos_reschedule(myself, FALSE);
    _set_current(next_thread);
  
    /* No need for sos_restore_IRQs() here because the IRQ flag will be
       restored to that of the next thread upon context switch */
  
    /* Immediate switch to next thread */
    sos_cpu_kstate_exit_to(next_thread->cpu_kstate,
                           (sos_cpu_kstate_function_arg1_t*) delete_thread,
                           (sos_ui32_t) myself);
  }
  
  
  sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr)
  {
    if (! thr)
      thr = (struct sos_kthread*)current_kthread;
  
    return thr->state;
  }
  
  
  typedef enum { YIELD_MYSELF, BLOCK_MYSELF } switch_type_t;
  /**
   * Helper function to initiate a context switch in case the current
   * thread becomes blocked, waiting for a timeout, or calls yield.
   */
  static sos_ret_t _switch_to_next_thread(switch_type_t operation)
  {
    struct sos_kthread *myself, *next_thread;
  
    SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
  
    /* Interrupt handlers are NOT allowed to block ! */
    SOS_ASSERT_FATAL(! sos_servicing_irq());
  
    myself = (struct sos_kthread*)current_kthread;
  
    /* Make sure that if we are to be marked "BLOCKED", we have any
       reason of effectively being blocked */
    if (BLOCK_MYSELF == operation)
      {
        myself->state = SOS_KTHR_BLOCKED;
      }
  
    /* Identify the next thread */
    next_thread = sos_reschedule(myself, YIELD_MYSELF == operation);
  
    /* Avoid context switch if the context does not change */
    if (myself != next_thread)
      {
        /* Sanity checks for the next thread */
        sos_cpu_kstate_detect_stack_overflow(next_thread->cpu_kstate,
                                             next_thread->stack_base_addr,
                                             next_thread->stack_size);
        
        /* Actual context switch */
        _set_current(next_thread);
        sos_cpu_kstate_switch(& myself->cpu_kstate, next_thread->cpu_kstate);
        
        /* Back here ! */
        SOS_ASSERT_FATAL(current_kthread == myself);
        SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING);
      }
    else
      {
        /* No context switch but still update ID of current thread */
        _set_current(next_thread);
      }
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_kthread_yield()
  {
    sos_ui32_t flags;
    sos_ret_t retval;
  
    sos_disable_IRQs(flags);
  
    retval = _switch_to_next_thread(YIELD_MYSELF);
  
    sos_restore_IRQs(flags);
    return retval;
  }
  
  
  /**
   * Internal sleep timeout management
   */
  struct sleep_timeout_params
  {
    struct sos_kthread *thread_to_wakeup;
    sos_bool_t timeout_triggered;
  };
  
  
  /**
   * Callback called when a timeout happened
   */
  static void sleep_timeout(struct sos_timeout_action *act)
  {
    struct sleep_timeout_params *sleep_timeout_params
      = (struct sleep_timeout_params*) act->routine_data;
  
    /* Signal that we have been woken up by the timeout */
    sleep_timeout_params->timeout_triggered = TRUE;
  
    /* Mark the thread ready */
    SOS_ASSERT_FATAL(SOS_OK ==
                     sos_kthread_force_unblock(sleep_timeout_params
                                                 ->thread_to_wakeup));
  }
  
  
  sos_ret_t sos_kthread_sleep(struct sos_time *timeout)
  {
    sos_ui32_t flags;
    struct sleep_timeout_params sleep_timeout_params;
    struct sos_timeout_action timeout_action;
    sos_ret_t retval;
  
    /* Block forever if no timeout is given */
    if (NULL == timeout)
      {
        sos_disable_IRQs(flags);
        retval = _switch_to_next_thread(BLOCK_MYSELF);
        sos_restore_IRQs(flags);
  
        return retval;
      }
  
    /* Initialize the timeout action */
    sos_time_init_action(& timeout_action);
  
    /* Prepare parameters used by the sleep timeout callback */
    sleep_timeout_params.thread_to_wakeup 
      = (struct sos_kthread*)current_kthread;
    sleep_timeout_params.timeout_triggered = FALSE;
  
    sos_disable_IRQs(flags);
  
    /* Now program the timeout ! */
    SOS_ASSERT_FATAL(SOS_OK ==
                     sos_time_register_action_relative(& timeout_action,
                                                       timeout,
                                                       sleep_timeout,
                                                       & sleep_timeout_params));
  
    /* Prepare to block: wait for sleep_timeout() to wakeup us in the
       timeout kwaitq, or for someone to wake us up in any other
       waitq */
    retval = _switch_to_next_thread(BLOCK_MYSELF);
    /* Unblocked by something ! */
  
    /* Unblocked by timeout ? */
    if (sleep_timeout_params.timeout_triggered)
      {
        /* Yes */
        SOS_ASSERT_FATAL(sos_time_is_zero(& timeout_action.timeout));
        retval = SOS_OK;
      }
    else
      {
        /* No: We have probably been woken up while in some other
           kwaitq */
        SOS_ASSERT_FATAL(SOS_OK == sos_time_unregister_action(& timeout_action));
        retval = -SOS_EINTR;
      }
  
    sos_restore_IRQs(flags);
  
    /* Update the remaining timeout */
    memcpy(timeout, & timeout_action.timeout, sizeof(struct sos_time));
  
    return retval;
  }
  
  
  sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread)
  {
    sos_ret_t retval;
    sos_ui32_t flags;
  
    if (! kthread)
      return -SOS_EINVAL;
    
    sos_disable_IRQs(flags);
  
    /* Thread already woken up ? */
    retval = SOS_OK;
    switch(sos_kthread_get_state(kthread))
      {
      case SOS_KTHR_RUNNING:
      case SOS_KTHR_READY:
        /* Do nothing */
        break;
  
      case SOS_KTHR_ZOMBIE:
        retval = -SOS_EFATAL;
        break;
  
      default:
        retval = sos_sched_set_ready(kthread);
        break;
      }
  
    sos_restore_IRQs(flags);
  
    return retval;
  }
  
 

/tmp/sos-code-article6/sos/kthread.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/kthread.h (2005-01-04 04:12:16.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_KTHREAD_H_
  #define _SOS_KTHREAD_H_
  
  #include <sos/errno.h>
  
  /**
   * @file kthread.h
   *
   * SOS Kernel thread management API
   */
  
  
  /* Forward declaration */
  struct sos_kthread;
  
  #include <hwcore/cpu_context.h>
  #include <sos/sched.h>
  #include <sos/kwaitq.h>
  #include <sos/time.h>
  
  
  /**
   * The possible states of a valid kernel thread
   */
  typedef enum { SOS_KTHR_CREATED, /**< Thread created, not fully initialized */
                 SOS_KTHR_READY,   /**< Thread fully initialized or waiting
                                        for CPU after having been blocked */
                 SOS_KTHR_RUNNING, /**< Thread currently running on CPU */
                 SOS_KTHR_BLOCKED, /**< Thread waiting for I/O (+ in at LEAST
                                        one kwaitq) and/or sleeping (+ in NO
                                        kwaitq) */
                 SOS_KTHR_ZOMBIE,  /**< Thread terminated execution, waiting to
                                        be deleted by kernel */
               } sos_kthread_state_t;
  
  
  /**
   * TCB (Thread Control Block): structure describing a Kernel
   * thread. Don't access these fields directly: prefer using the
   * accessor functions below.
   */
  struct sos_kthread
  {
  #define SOS_KTHR_MAX_NAMELEN 32
    char name[SOS_KTHR_MAX_NAMELEN];
  
    sos_kthread_state_t  state;
  
    /* The hardware context of the thread */
    struct sos_cpu_kstate *cpu_kstate;
    sos_vaddr_t stack_base_addr;
    sos_size_t  stack_size;
  
    /* Data specific to each state */
    union
    {
      struct
      {
        struct sos_kthread     *rdy_prev, *rdy_next;
      } ready;
    }; /* Anonymous union (gcc extenion) */
  
  
    /*
     * Data used by the kwaitq subsystem: list of kwaitqueues the thread
     * is waiting for.
     *
     * @note: a RUNNING or READY thread might be in one or more
     * waitqueues ! The only property we have is that, among these
     * waitqueues (if any), _at least_ one has woken the thread.
     */
    struct sos_kwaitq_entry *kwaitq_list;
  
  
    /**
     * Chaining pointers for global ("gbl") list of threads (debug)
     */
    struct sos_kthread *gbl_prev, *gbl_next;
  };
  
  
  /**
   * Definition of the function executed by a kernel thread
   */
  typedef void (*sos_kthread_start_routine_t)(void *arg);
  
  
  /**
   * Initialize the subsystem responsible for kernel thread management
   *
   * Initialize primary kernel thread so that it can be handled the same
   * way as an ordinary thread created by sos_kthread_create().
   */
  sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr,
                                        sos_size_t init_thread_stack_size);
  
  
  /**
   * Create a new kernel thread
   */
  struct sos_kthread *sos_kthread_create(const char *name,
                                         sos_kthread_start_routine_t start_func,
                                         void *start_arg);
  
  
  /**
   * Terminate the execution of the current thread. Called by default
   * when the start routine returns.
   */
  void sos_kthread_exit() __attribute__((noreturn));
  
  
  /**
   * Get the identifier of the thread currently running on CPU. Trivial
   * function.
   */
  struct sos_kthread *sos_kthread_get_current();
  
  
  /**
   * If thr == NULL, get the state of the current thread. Trivial
   * function.
   *
   * @note NOT protected against interrupts
   */
  sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr);
  
  
  /**
   * Yield CPU to another ready thread.
   *
   * @note This is a BLOCKING FUNCTION
   */
  sos_ret_t sos_kthread_yield();
  
  
  /**
   * Release the CPU for (at least) the given delay.
   *
   * @param delay The delay to wait for. If delay == NULL then wait
   * forever that any event occurs.
   *
   * @return SOS_OK when delay expired (and delay is reset to zero),
   * -SOS_EINTR otherwise (and delay contains the amount of time
   * remaining).
   *
   * @note This is a BLOCKING FUNCTION
   */
  sos_ret_t sos_kthread_sleep(/* in/out */struct sos_time *delay);
  
  
  /**
   * Mark the given thread as READY (if not already ready) even if it is
   * blocked in a kwaitq or in a sleep ! As a result, the interrupted
   * kwaitq/sleep function call of the thread will return with
   * -SOS_EINTR.
   *
   * @return -SOS_EINVAL if thread does not exist, or -SOS_EFATAL if
   * marked ZOMBIE.
   *
   * @note As a result, the semaphore/mutex/conditions/... functions
   * return values SHOULD ALWAYS be checked ! If they are != SOS_OK,
   * then the caller should consider that the resource is not aquired
   * because somebody woke the thread by some way.
   */
  sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread);
  
  
  #endif /* _SOS_KTHREAD_H_ */
  
 

/tmp/sos-code-article6/sos/kwaitq.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/kwaitq.c (2005-01-04 04:12:16.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->kthread = sos_kthread_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->kthread);
  
    /* (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->kthread->kwaitq_list, kwq_entry,
                        prev_entry_for_kthread, next_entry_for_kthread);
  
    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->kthread->kwaitq_list, kwq_entry,
                      prev_entry_for_kthread, next_entry_for_kthread);
  
    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_kthread_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_kthreads,
                              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_kthreads), 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 kthreads woken up ? */
        if (nb_kthreads <= 0)
          break;
  
        /*
         * Ok: wake up the thread for this entry
         */
  
        /* Thread already woken up ? */
        if (SOS_KTHR_RUNNING == sos_kthread_get_state(kwq_entry->kthread))
          {
            /* 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->kthread);
          }
  
        /* Remove this waitq entry */
        _kwaitq_remove_entry(kwq, kwq_entry);
        kwq_entry->wakeup_triggered = TRUE;
        kwq_entry->wakeup_status    = wakeup_status;
  
        /* Next iteration... */
        nb_kthreads --;
      }
  
    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-01-04 04:12:16.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/kthread.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_kthread *kthread;
  
    /** 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_kthread, *next_entry_for_kthread;  
  };
  
  
  /**
   * 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
   * "kthread" 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_kthread 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_kthreads,
                              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-01-04 04:12:15.000000000 +0100 )
Line 29 
Line 29 
 #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/kthread.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 66 
Line 68 
                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 180 
Line 185 
 } }
  
  
  
 /* ====================================================================== 
  * 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: */ 
       /* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */ 
     } 
   else 
     test_backtrace(i-1, magic, stack_bottom, stack_size); 
 } 
  
 /* ======================================================================   int col;
  * Parsing of Mathematical expressions   int row;
  * };
  * 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) 
 }   struct thr_arg *thr_arg = (struct thr_arg*)arg;
 static void func_exit(sos_ui32_t unused)   int progress = 0;
 { 
   sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0); 
 } 
  
  
 /* 
  * The reader coroutine and associated variable. This coroutine could 
  * 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)   sos_bochs_printf("start %c", thr_arg->character);
 {   while (1)
   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_kthread_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; 
           data_lexer_to_parser.operator = c; 
         } 
       else if (got_what == GOT_NUM) 
           data_lexer_to_parser.type = LEX_IS_NUMBER;           struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 };
           data_lexer_to_parser.number *= 10;           sos_bochs_printf("sleep1(%c)\n", thr_arg->character);
           data_lexer_to_parser.number += (c - '0');           sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's');
            SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t));
            SOS_ASSERT_FATAL(sos_time_is_zero(& t));
            sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R');
       else if (got_what == GOT_STR) 
        /* Go to sleep for a longer time some other times... */
        else if ((random() % 300) == 0)
           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_kthread_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_kthread()
  * 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_kthread_create("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_kthread_create("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_kthread_create("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_kthread_create("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_kthread_create("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_kthread_create("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_kthread()
   const struct syntax_node *e;   sos_ui32_t idle_twiddle = 0;
   const char **var_name; 
   int *var_val; 
   int nb_vars; 
   int result;   while (1)
 }; 
  
 static void func_eval(struct func_eval_params *parms) 
 { 
   /* 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_kthread_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  * The C entry point of our operating system
  */ 
 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 
  * 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 1015 
Line 345 
   sos_bochs_putstring("Message in a bochs\n");   sos_bochs_putstring("Message in a bochs\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 388 
   /* 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 398 
  
   /* 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);
Line 1078 
Line 410 
    * 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_kthread_subsystem_setup(bootstrap_stack_bottom,
    * Print hello world using coroutines                               bootstrap_stack_size);
    */ 
   print_hello_world(); 
    /* Initialize the scheduler */
    sos_sched_subsystem_setup();
  
   /*   /* Declare the IDLE thread */
    * Run coroutine tests   SOS_ASSERT_FATAL(sos_kthread_create("idle", idle_kthread, NULL) != NULL);
    */ 
   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)"); 
     } 
   /*   /* Enabling the HW interrupts here, this will make the timer HW
    * Run some demand-paging tests      interrupt call the scheduler */
    */   asm volatile ("sti\n");
   test_demand_paging(234567, 500); 
  
  
   /* 
    * Create an un-resolved page fault, which will make the page fault 
    * handler print the backtrace. 
    */ 
   test_backtrace(6, 0xdeadbeef, bootstrap_stack_bottom, bootstrap_stack_size); 
   /*   /*
    * System should be halted BEFORE here !    * Force the idle thread to run at least once to force a context
    */    * switch. This way the "cpu_kstate" of the kernel thread for the
     * sos_main thread gets a chance to be filled with the current CPU
     * context. Useful only if we call sos_kthread_exit() too early from
   /* An operatig system never ends */    * sos_main: a "stack overflow" will be wrongly detected simply
   for (;;)    * because the "cpu_kstate" of the thread has not be correctly
     continue;    * initialised. A context switch is a good way to initialise it.
     */
   return;   sos_kthread_yield();
  
  
    /* Now run some Kernel threads just for fun ! */
    extern void MouseSim();
    MouseSim(); 
    test_kthread();
  
    /*
     * We can safely exit from this function now, for there is already
     * an idle Kernel thread ready to make the CPU busy working...
     *
     * However, we must EXPLICITELY call sos_kthread_exit() because a
     * simple "return" will return nowhere ! Actually this first thread
     * was initialized by the Grub bootstrap stage, at a time when the
     * word "thread" did not exist. This means that the stack was not
     * setup in order for a return here to call sos_kthread_exit()
     * automagically. Hence we must call it manually. This is the ONLY
     * kernel thread where we must do this manually.
     */
    sos_bochs_printf("Bye from primary thread !\n");
    sos_kthread_exit();
    SOS_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-01-04 04:12:16.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/kthread.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_kthread *        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);
  }
  
  
  void ThreadDelete(struct sos_kthread *tid)
  {
    SOS_ASSERT_FATAL(! "TODO !");
  }
  
  //*****************************************************************************
  // 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_kthread_create("MouseCreator",
                                        (sos_kthread_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_kthread_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_kthread_create("Mouse", (sos_kthread_start_routine_t)Mouse, pElement);
                                          if(pElement->ThreadID == 0)
                                          {
                                                  sos_kfree((sos_vaddr_t)pElement);
                                                  pElement = NULL;
                                          }
                                          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-01-04 04:12:15.000000000 +0100 )
Line 59 
Line 59 
 /** 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-01-04 04:12:16.000000000 +0100 )
Line 72 
Line 72 
  * 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-01-04 04:12:16.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/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_kthread *kthread_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_kthread *thr,
                                      sos_bool_t insert_at_tail)
  {
  
    SOS_ASSERT_FATAL( (SOS_KTHR_CREATED == thr->state)
                      || (SOS_KTHR_RUNNING == thr->state) /* Yield */
                      || (SOS_KTHR_BLOCKED == thr->state) );
  
    /* Add the thread to the CPU queue */
    if (insert_at_tail)
      list_add_tail_named(ready_queue.kthread_list, thr,
                          ready.rdy_prev, ready.rdy_next);
    else
      list_add_head_named(ready_queue.kthread_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_KTHR_READY;
  
    return SOS_OK;
  }
  
  
  sos_ret_t sos_sched_set_ready(struct sos_kthread *thr)
  {
    sos_ret_t retval;
  
    /* Don't do anything for already ready threads */
    if (SOS_KTHR_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_kthread * sos_reschedule(struct sos_kthread *current_kthread,
                                      sos_bool_t do_yield)
  {
  
    if (SOS_KTHR_ZOMBIE == current_kthread->state)
      {
        /* Don't think of returning to this thread since it is
           terminated */
        /* Nop */
      }
    else if (SOS_KTHR_BLOCKED != current_kthread->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_kthread, TRUE);
        else
          /* Put it at the head of the active list */
          add_in_ready_queue(current_kthread, FALSE);
      }
  
    /* The next thread is that at the head of the ready list */
    if (ready_queue.nr_threads > 0)
      {
        struct sos_kthread *next_thr;
  
        /* Queue is not empty: take the thread at its head */
        next_thr = list_pop_head_named(ready_queue.kthread_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-01-04 04:12:16.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 kthread.h for that) or synchronizations
   * (see kwaitq.h or the higher levels primitives [mutex, semaphore,
   * ...] for that).
   *
   * @note IMPORTANT: all the functions below are meant to be called
   * ONLY by the kthread/timer/kwaitq subsystems. DO NOT use them
   * directly from anywhere else: use ONLY the kthread/kwaitq functions!
   * If you still want to call them directly despite this disclaimer,
   * simply disable interrupts before clling them.
   */
  
  #include <sos/errno.h>
  
  
  #include <sos/kthread.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_kthread * thr);
  
  
  /**
   * Return the identifier of the next kthread to run. Also removes it
   * from the ready list, but does NOT set is as current_kthread !
   *
   * @param current_kthread TCB of the thread calling the function
   *
   * @param do_yield When TRUE, put the current executing thread at the
   * end of the ready list. Otherwise it is kept at the head of it.
   *
   * @note: The use of this function is RESERVED
   */
  struct sos_kthread * sos_reschedule(struct sos_kthread * current_kthread,
                                      sos_bool_t do_yield);
  
  #endif /* _SOS_WAITQUEUE_H_ */
  
 

/tmp/sos-code-article6/sos/time.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6.5/sos/time.c (2005-01-04 04:12:16.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 <sos/kthread.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-01-04 04:12:16.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/VERSION (2004-12-03 16:27:45.000000000 +0100 )
../sos-code-article6.5/VERSION (2005-01-04 04:12:14.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


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

File list:
    
../sos-code-article6.5/drivers/bochs.c
    ../sos-code-article6.5/drivers/bochs.h
    ../sos-code-article6.5/hwcore/cpu_context.c
    ../sos-code-article6.5/hwcore/exception.c
    ../sos-code-article6.5/hwcore/exception.h
    ../sos-code-article6.5/hwcore/gdt.c
    ../sos-code-article6.5/hwcore/gdt.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/irq.c
    ../sos-code-article6.5/hwcore/irq.h
    ../sos-code-article6.5/hwcore/paging.c
    ../sos-code-article6.5/hwcore/paging.h
    ../sos-code-article6.5/Makefile
    ../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/kthread.c
    ../sos-code-article6.5/sos/kthread.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/time.c
    ../sos-code-article6.5/sos/time.h
    ../sos-code-article6.5/VERSION