/tmp/sos-code-article5/Makefile (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/Makefile (2005-01-11 09:32:39.000000000 +0100 )
Line 7 
Line 7 
           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o        \           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o        \
           hwcore/paging.o                                        \           hwcore/paging.o                                        \
           hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o        \           hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.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/main.o           sos/physmem.o sos/klibc.o                                \
            sos/assert.o sos/main.o
 KERNEL_OBJ   = sos.elf KERNEL_OBJ   = sos.elf
 MULTIBOOT_IMAGE = fd.img MULTIBOOT_IMAGE = fd.img
Line 23 
Line 25 
 $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds
         $(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS)         $(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS)
         -nm -C $@ | cut -d ' ' -f 1,3 > sos.map         -nm -C $@ | cut -d ' ' -f 1,3 > sos.map
          size $@
  
 -include .mkvars -include .mkvars
  
  
 

/tmp/sos-code-article5/VERSION (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/VERSION (2005-01-11 09:32:39.000000000 +0100 )
Line 1 
Line 1 
 SOS -- Simple OS SOS -- Simple OS
 Copyright (C) 2003,2004 The SOS Team (David Decotigny & Thomas Petazzoni) Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni)
 Version "Article 5" -- Kernel space virtual memory allocator (Slab system) Version "Article 6 (1st part)" -- Low-level kernel thread management
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/tmp/sos-code-article5/bootstrap/multiboot.S (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/bootstrap/multiboot.S (2005-01-11 09:32:39.000000000 +0100 )
Line 36 
Line 36 
   /* checksum=     */ .long -(MULTIBOOT_HEADER_MAGIC \   /* checksum=     */ .long -(MULTIBOOT_HEADER_MAGIC \
                               +MULTIBOOT_HEADER_FLAGS)                               +MULTIBOOT_HEADER_FLAGS)
   /* header_addr=  */ .long multiboot_header   /* header_addr=  */ .long multiboot_header
   /* load_addr=    */ .long __b_kernel   /* load_addr=    */ .long __b_load
   /* bss_end_addr= */ .long __e_kernel   /* bss_end_addr= */ .long __e_kernel
   /* entry_addr=   */ .long multiboot_entry   /* entry_addr=   */ .long multiboot_entry
Line 70 
Line 70 
         hlt         hlt
         jmp loop         jmp loop
  
         /* Here is the stack */ /* Here is the stack */
 .comm   stack, MULTIBOOT_STACK_SIZE .section ".init_stack", "aw", @nobits
  .size stack, MULTIBOOT_STACK_SIZE
  stack:
          .space MULTIBOOT_STACK_SIZE
  
  /* Some data characterizing the stack addresses */
  .data
          .globl bootstrap_stack_bottom
  bootstrap_stack_bottom: .long stack
  
          .globl bootstrap_stack_size
  bootstrap_stack_size: .long MULTIBOOT_STACK_SIZE
 

/tmp/sos-code-article5/bootstrap/multiboot.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/bootstrap/multiboot.h (2005-01-11 09:32:39.000000000 +0100 )
Line 43 
Line 43 
 #endif #endif
  
 #ifndef ASM #ifndef ASM
 /* Do not include here in boot.S.  */ /* Do not include here in the assembler sources.  */
  #include <sos/types.h>
  
  /* The address of the stack of the bootstrap thread */
  extern sos_vaddr_t bootstrap_stack_bottom;
  extern sos_size_t bootstrap_stack_size;
  
 /* Types.  */ /* Types.  */
  
  
 

/tmp/sos-code-article5/drivers/bochs.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/drivers/bochs.c (2005-01-11 09:32:39.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-article5/drivers/bochs.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/drivers/bochs.h (2005-01-11 09:32:39.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-article5/extra/bootsect.S (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/extra/bootsect.S (2005-01-11 09:32:39.000000000 +0100 )
Line 1 
Line 1 
  
 /* /*
  * @(#) $Id: bootsect.S,v 1.6 2004/06/18 07:43:51 d2 Exp $  * @(#) $Id: bootsect.S,v 1.8 2004/11/20 16:00:11 d2 Exp $
  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
  *           Jerome Petazzoni & Bernard Cassagne & coffeeman  *           Jerome Petazzoni & Bernard Cassagne & coffeeman
Line 386 
Line 386 
  * sos_bsect.lds ***/  * sos_bsect.lds ***/
  
 /* La pile de 16k qu'on utilise au niveau de LaunchKernel se trouve /* La pile de 16k qu'on utilise au niveau de LaunchKernel se trouve
    declaree avec le noyau, dans sa section ".bss", cad HORS du boot    declaree avec le noyau, dans sa section ".init_stack", cad HORS du boot
    définir directement dans le sos_bsect.lds, ou dans un fichier .c    définir directement dans le sos_bsect.lds, ou dans un fichier .c
    auxiliaire pour plus de clarté */    auxiliaire pour plus de clarté */
 .comm   stack, BOOT_STACK_SIZE /* Here is the stack */
  .section ".init_stack", "aw", @nobits
  .p2align 4
  .size stack, BOOT_STACK_SIZE
  stack:
          .space BOOT_STACK_SIZE
  
  /* Some data characterizing the stack addresses */
  .data
          .globl bootstrap_stack_bottom
  bootstrap_stack_bottom: .long stack
  
          .globl bootstrap_stack_size
  bootstrap_stack_size: .long BOOT_STACK_SIZE
 

/tmp/sos-code-article5/hwcore/cpu_context.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6/hwcore/cpu_context.c (2005-01-11 09:32:39.000000000 +0100 )
(New file) 
Line 1 
  /* Copyright (C) 2000-2004, The KOS team
     Copyright (C) 1999  Free Software Foundation, Inc.
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  
  
  #include <sos/assert.h>
  #include <sos/klibc.h>
  #include <drivers/bochs.h>
  #include <drivers/x86_videomem.h>
  #include <hwcore/segment.h>
  
  #include "cpu_context.h"
  
  
  /**
   * Here is the definition of a CPU context for IA32 processors. This
   * is a SOS convention, not a specification given by the IA32
   * spec. However there is a strong constraint related to the x86
   * interrupt handling specification: the top of the stack MUST be
   * compatible with the 'iret' instruction, ie there must be the
   * err_code (might be 0), eip, cs and eflags of the destination
   * context in that order (see Intel x86 specs vol 3, figure 5-4).
   *
   * @note IMPORTANT: This definition MUST be consistent with the way
   * the registers are stored on the stack in
   * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.
   */
  struct sos_cpu_kstate {
    /* (Lower addresses) */
  
    /* These are SOS convention */
    sos_ui16_t  gs;
    sos_ui16_t  fs;
    sos_ui16_t  es;
    sos_ui16_t  ds;
    sos_ui16_t  ss;
    sos_ui16_t  alignment_padding; /* unused */
    sos_ui32_t  eax;
    sos_ui32_t  ebx;
    sos_ui32_t  ecx;
    sos_ui32_t  edx;
    sos_ui32_t  esi;
    sos_ui32_t  edi;
    sos_ui32_t  ebp;
  
    /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
    sos_ui32_t  error_code;
    sos_vaddr_t eip;
    sos_ui32_t  cs;
    sos_ui32_t  eflags;
  
    /* (Higher addresses) */
  } __attribute__((packed));
  
  
  static void core_routine (sos_cpu_kstate_function_arg1_t *start_func,
                            sos_ui32_t start_arg,
                            sos_cpu_kstate_function_arg1_t *exit_func,
                            sos_ui32_t exit_arg)
       __attribute__((noreturn));
  
  static void core_routine (sos_cpu_kstate_function_arg1_t *start_func,
                            sos_ui32_t start_arg,
                            sos_cpu_kstate_function_arg1_t *exit_func,
                            sos_ui32_t exit_arg)
  {
    start_func(start_arg);
    exit_func(exit_arg);
  
    SOS_ASSERT_FATAL(! "The exit function of the thread should NOT return !");
    for(;;);
  }
  
  
  sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt,
                                sos_cpu_kstate_function_arg1_t *start_func,
                                sos_ui32_t  start_arg,
                                sos_vaddr_t stack_bottom,
                                sos_size_t  stack_size,
                                sos_cpu_kstate_function_arg1_t *exit_func,
                                sos_ui32_t  exit_arg)
  {
    /* This is a critical internal function, so that it is assumed that
       the caller knows what he does: we legitimally assume that values
       for ctxt, start_func, stack_* and exit_func are allways VALID ! */
  
    /* Setup the stack.
     *
     * On x86, the stack goes downward. Each frame is configured this
     * way (higher addresses first):
     *
     *  - (optional unused space. As of gcc 3.3, this space is 24 bytes)
     *  - arg n
     *  - arg n-1
     *  - ...
     *  - arg 1
     *  - return instruction address: The address the function returns to
     *    once finished
     *  - local variables
     *
     * The remaining of the code should be read from the end upward to
     * understand how the processor will handle it.
     */
  
    sos_vaddr_t tmp_vaddr = stack_bottom + stack_size;
    sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr;
  
    /* If needed, poison the stack */
  #ifdef SOS_CPU_KSTATE_DETECT_UNINIT_VARS
    memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, stack_size);
  #elif defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW)
    sos_cpu_kstate_prepare_detect_stack_overflow(stack_bottom, stack_size);
  #endif
  
    /* Simulate a call to the core_routine() function: prepare its
       arguments */
    *(--stack) = exit_arg;
    *(--stack) = (sos_ui32_t)exit_func;
    *(--stack) = start_arg;
    *(--stack) = (sos_ui32_t)start_func;
    *(--stack) = 0; /* Return address of core_routine => force page fault */
  
    /*
     * Setup the initial context structure, so that the CPU will execute
     * the function core_routine() once this new context has been
     * restored on CPU
     */
  
    /* Compute the base address of the structure, which must be located
       below the previous elements */
    tmp_vaddr  = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate);
    *ctxt = (struct sos_cpu_kstate*)tmp_vaddr;
  
    /* Initialize the CPU context structure */
    memset(*ctxt, 0x0, sizeof(struct sos_cpu_kstate));
  
    /* Tell the CPU context structure that the first instruction to
       execute will be that of the core_routine() function */
    (*ctxt)->eip = (sos_ui32_t)core_routine;
  
    /* Setup the segment registers */
    (*ctxt)->cs  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KCODE); /* Code */
    (*ctxt)->ds  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */
    (*ctxt)->es  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */
    (*ctxt)->ss  = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Stack */
    /* fs and gs unused for the moment. */
  
    /* The newly created context is initially interruptible */
    (*ctxt)->eflags = (1 << 9); /* set IF bit */
  
    return SOS_OK;
  }
  
  
  #if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW)
  void
  sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
                                               sos_vaddr_t stack_bottom,
                                               sos_size_t stack_size)
  {
    sos_size_t poison_size = SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW;
    if (poison_size > stack_size)
      poison_size = stack_size;
  
    memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, poison_size);
  }
  
  
  void
  sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
                                       sos_vaddr_t stack_bottom,
                                       sos_size_t stack_size)
  {
    unsigned char *c;
    int i;
  
    SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) >= stack_bottom);
    SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate)
                     <= stack_bottom + stack_size);
    for (c = (unsigned char*) stack_bottom, i = 0 ;
         (i < SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) && (i < stack_size) ;
         c++, i++)
      {
        SOS_ASSERT_FATAL(SOS_CPU_KSTATE_STACK_POISON == *c);
      }
  }
  #endif
  
  
  sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt)
  {
    SOS_ASSERT_FATAL(NULL != ctxt);
    return ctxt->eip;
  }
  
  
  sos_vaddr_t sos_cpu_kstate_get_SP(const struct sos_cpu_kstate *ctxt)
  {
    SOS_ASSERT_FATAL(NULL != ctxt);
    return (sos_vaddr_t)ctxt;
  }
  
  
  void sos_cpu_kstate_dump(const struct sos_cpu_kstate *ctxt)
  {
    char buf[128];
    snprintf(buf, sizeof(buf),
             "CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x",
             (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags,
             (unsigned)ctxt->cs, (unsigned)ctxt->ds, (unsigned)ctxt->ss,
             (unsigned)ctxt->error_code);
    sos_bochs_putstring(buf); sos_bochs_putstring("\n");
    sos_x86_videomem_putstring(23, 0,
                            SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY,
                            buf);
  }
  
  
  sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt)
  {
    SOS_ASSERT_FATAL(NULL != ctxt);
    return ctxt->error_code;
  }
  
  
  sos_vaddr_t
  sos_cpu_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *ctxt)
  {
    sos_ui32_t cr2;
  
    /* See Intel Vol 3 (section 5.14): the address of the faulting
       virtual address of a page fault is stored in the cr2 register */
    asm volatile ("movl %%cr2, %0"
                  :"=r"(cr2)
                  : );
  
    return cr2;
  }
  
  
  sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate,
                           sos_ui32_t max_depth,
                           sos_vaddr_t stack_bottom,
                           sos_size_t stack_size,
                           sos_backtrace_callback_t * backtracer,
                           void *custom_arg)
  {
    int depth;
    sos_vaddr_t callee_PC, caller_frame;
  
    /*
     * Layout of a frame on the x86 (compiler=gcc):
     *
     * funcA calls funcB calls funcC
     *
     *         ....
     *         funcB Argument 2
     *         funcB Argument 1
     *         funcA Return eip
     * frameB: funcA ebp (ie previous stack frame)
     *         ....
     *         (funcB local variables)
     *         ....
     *         funcC Argument 2
     *         funcC Argument 1
     *         funcB Return eip
     * frameC: funcB ebp (ie previous stack frame == A0) <---- a frame address
     *         ....
     *         (funcC local variables)
     *         ....
     *
     * The presence of "ebp" on the stack depends on 2 things:
     *   + the compiler is gcc
     *   + the source is compiled WITHOUT the -fomit-frame-pointer option
     * In the absence of "ebp", chances are high that the value pushed
     * at that address is outside the stack boundaries, meaning that the
     * function will return -SOS_ENOSUP.
     */
  
    if (cpu_kstate)
      {
        callee_PC    = cpu_kstate->eip;
        caller_frame = cpu_kstate->ebp;
      }
    else
      {
        /* Skip the sos_backtrace() frame */
        callee_PC    = (sos_vaddr_t)__builtin_return_address(0);
        caller_frame = (sos_vaddr_t)__builtin_frame_address(1);
      }
  
    for(depth=0 ; depth < max_depth ; depth ++)
      {
        /* Call the callback */
        backtracer(callee_PC, caller_frame + 8, depth, custom_arg);
  
        /* If the frame address is funky, don't go further */
        if ( (caller_frame < stack_bottom)
             || (caller_frame + 4 >= stack_bottom + stack_size) )
          return depth;
  
        /* Go to caller frame */
        callee_PC    = *((sos_vaddr_t*) (caller_frame + 4));
        caller_frame = *((sos_vaddr_t*) caller_frame);
      }
    
    return depth;
  }
  
 

/tmp/sos-code-article5/hwcore/cpu_context.h (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6/hwcore/cpu_context.h (2005-01-11 09:32:39.000000000 +0100 )
(New file) 
Line 1 
  /* Copyright (C) 2000-2004, The KOS team
     Copyright (C) 1999  Free Software Foundation, Inc.
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  #ifndef _SOS_CPUCTXT_H_
  #define _SOS_CPUCTXT_H_
  
  
  /**
   * @file cpu_context.h
   *
   * Low level API to manage kernel and user thread CPU contexts. Should
   * be some kind of architecture-independent.
   */
  
  #include <sos/types.h>
  #include <sos/errno.h>
  
  
  /**
   * Opaque structure storing the CPU context of an inactive kernel
   * thread, as saved by the low level primitives below or by the
   * interrupt/exception handlers.
   *
   * @note This is an (architecture-independent) forward declaration:
   * see cpu_context.c and the *.S files for its
   * (architecture-dependent) definition.
   */
  struct sos_cpu_kstate;
  
  
  /**
   * The type of the functions passed as arguments below
   */
  typedef void (sos_cpu_kstate_function_arg1_t(sos_ui32_t arg1));
  
  
  /**
   * Function to create an initial context for a kernel thread starting
   * its execution at function start_func with the argument initial_arg,
   * and having the stack defined by stack_bottom/stack_size. When the
   * start_func function returns, the function exit_func is called with
   * argument exit_arg.
   *
   * @param ctxt The kernel thread CPU context to initialize. The
   * address of the newly-initialized struct sos_cpu_kstate will be
   * stored in this variable. The contents of this struct sos_cpu_kstate
   * are actually located /inside/ the stack.
   *
   * @param start_func The address of the first instruction that will be
   * executed when this context will be first transferred on
   * CPU. Practically speaking, this is the address of a function that
   * is assumed to take 1 argument.
   *
   * @param start_arg The value that will be passed as the argument to
   * start_func when the thread starts. The stack will be setup
   * accordingly to simulate a real call to the function and really
   * passing this arguement.
   *
   * @param stack_bottom The lowest address of the stack.
   *
   * @param stack_size The size of the stack.
   *
   * @param exit_func The address of the instruction executed after the
   * function start_func has returned. This function takes 1 parameter
   * as argument: exit_arg.
   *
   * @param exit_arg The argument passed to the function exit_func.
   *
   * @note the newly created context is INTERRUPTIBLE by default !
   */
  sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt,
                                sos_cpu_kstate_function_arg1_t *start_func,
                                sos_ui32_t  start_arg,
                                sos_vaddr_t stack_bottom,
                                sos_size_t  stack_size,
                                sos_cpu_kstate_function_arg1_t *exit_func,
                                sos_ui32_t  exit_arg);
  
  
  /**
   * Function that performs an immediate context-switch from one kernel
   * thread to another one. It stores the current executing context in
   * from_ctxt, and restores to_context on CPU.
   *
   * @param from_ctxt The address of the struct sos_cpu_kstate will be
   * stored in this variable. Must NOT be NULL.
   *
   * @param to_ctxt The CPU will resume its execution with the struct
   * sos_cpu_kstate located at this address. Must NOT be NULL.
   */
  void sos_cpu_kstate_switch(struct sos_cpu_kstate **from_ctxt,
                             struct sos_cpu_kstate *to_ctxt);
  
  
  /*
   * Switch to the new given context (of a kernel thread) without saving
   * the old context (of another kernel thread), and call the function
   * reclaiming_func passing it the recalining_arg argument. The
   * reclaining function is called from within the stack of the new
   * context, so that it can (among other things) safely destroy the
   * stack of the former context.
   *
   * @param switch_to_ctxt The context that will be restored on the CPU
   *
   * @param reclaiming_func The address of the function that will be
   * called after having changed the stack, but before restoring the CPU
   * context to switch_to_ctxt.
   */
  void
  sos_cpu_kstate_exit_to(struct sos_cpu_kstate *switch_to_ctxt,
                         sos_cpu_kstate_function_arg1_t *reclaiming_func,
                         sos_ui32_t reclaiming_arg) __attribute__((noreturn));
  
  
  /* =======================================================================
   * Public Accessor functions
   */
  
  /**
   * Return Program Counter stored in the saved context
   */
  sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt);
  
  
  /**
   * Return Stack Pointer stored in the saved context
   */
  sos_vaddr_t sos_cpu_kstate_get_SP(const struct sos_cpu_kstate *ctxt);
  
  
  /**
   * Dump the contents of the CPU context (bochs + x86_videomem)
   */
  void sos_cpu_kstate_dump(const struct sos_cpu_kstate *ctxt);
  
  
  /* =======================================================================
   * Public Accessor functions TO BE USED ONLY BY Exception handlers
   */
  
  
  /**
   * Return the argument passed by the CPU upon exception, as stored in the
   * saved context
   */
  sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt);
  
  
  /**
   * Return the faulting address of the exception
   */
  sos_vaddr_t
  sos_cpu_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *ctxt);
  
  
  /* =======================================================================
   * Macros controlling stack poisoning.
   * Stack poisoning can be used to detect:
   *  - unitialized local variables
   *  - when the thread might have gone too deep in the stack
   */
  /** The signature of the poison */
  #define SOS_CPU_KSTATE_STACK_POISON 0xa5
  
  /**
   * When set, mean that the whole stack is poisoned to detect use of
   * unititialized variables
   */
  #define SOS_CPU_KSTATE_DETECT_UNINIT_VARS
  /* #undef SOS_CPU_KSTATE_DETECT_UNINIT_VARS */
  
  /**
   * When set, mean that the bottom of the stack is poisoned to detect
   * probable stack overflow. Its value indicates the number of bytes
   * used for this detection.
   */
  #define SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW  64
  /* #undef SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW */
  
  #if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW)
  void
  sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
                                               sos_vaddr_t stack_bottom,
                                               sos_size_t stack_size);
  void sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt,
                                            sos_vaddr_t stack_bottom,
                                            sos_size_t stack_size);
  #else
  # define sos_cpu_kstate_prepare_detect_stack_overflow(ctxt,stkbottom,stksize) \
    ({ /* nop */ })
  # define sos_cpu_kstate_detect_stack_overflow(ctxt,stkbottom,stksize) \
    ({ /* nop */ })
  #endif
  
  
  /* =======================================================================
   * Backtrace facility. To be used for DEBUGging purpose ONLY.
   */
  
  
  /**
   * The function called at each step of the backtrace iterations
   *
   * @param PC The address of the next instruction of the function that
   * will be executed
   *
   * @param params The address of the array of the parameteres that have
   * been passed to the function considered
   *
   * @param depth The index of the iteration (ie the depth of the
   * current frame into the stack)
   *
   * @param custom_arg Whatever you want: this is the argument passed as
   * custom_arg to sos_backtrace()
   */
  typedef void (sos_backtrace_callback_t)(sos_vaddr_t PC,
                                          sos_vaddr_t params,
                                          sos_ui32_t depth,
                                          void *custom_arg);
  
  
  /**
   * Call the backtracer callback on each frame stored in the cpu_kstate
   *
   * @param cpu_kstate The CPU context we want to explore. NULL to
   * backtrace the current CPU context.
   *
   * @param max_depth The maximum number of frames to explore
   *
   * @param stack_bottom The lower boundary of the stack. This is used
   * to make sure that the frame addresses fit inside the stack
   * boudaries (ie are potentially correct).
   *
   * @param stack_size The size of the stack. Same comment.
   *
   * @param backtracer The function to call to handle the frame for each
   * iteration
   *
   * @param custom_arg The arg passed as custom_arg to the backtracer
   *
   * @return The number of frames explored.
   *
   * @note Might be inaccurate when gcc's -fomit-frame-pointer has been
   * used.
   */
  sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate,
                           sos_ui32_t max_depth,
                           sos_vaddr_t stack_bottom,
                           sos_size_t stack_size,
                           sos_backtrace_callback_t * backtracer,
                           void *custom_arg);
  
  #endif /* _SOS_CPUCTXT_H_ */
  
 

/tmp/sos-code-article5/hwcore/cpu_context_switch.S (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6/hwcore/cpu_context_switch.S (2005-01-11 09:32:39.000000000 +0100 )
(New file) 
Line 1 
  /* Copyright (C) 2000-2004, The KOS team
     Copyright (C) 1999  Free Software Foundation, Inc.
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  #define ASM_SOURCE 1
           
  .file "cpu_context_switch.S"
  
  .text
  
          
  .globl sos_cpu_kstate_switch
  .type sos_cpu_kstate_switch, @function
  sos_cpu_kstate_switch:
          // arg2= to_context    --    esp+64
          // arg1= from_context  --    esp+60
          // caller ip           --    esp+56
    pushf              // (eflags)     esp+52
    pushl %cs          // (cs)         esp+48
    pushl $resume_pc   // (ip)         esp+44
    pushl $0           // (error code) esp+40
    pushl %ebp         //              esp+36
    pushl %edi         //              esp+32
    pushl %esi         //              esp+28
    pushl %edx         //              esp+24
    pushl %ecx         //              esp+20
    pushl %ebx         //              esp+16
    pushl %eax         //              esp+12
    subl $2, %esp      // (alignment)  esp+10
    pushw %ss          //              esp+8
    pushw %ds          //              esp+6
    pushw %es          //              esp+4
    pushw %fs          //              esp+2
    pushw %gs          //              esp
  
    /*
     * Now that the original eax/ebx are store, we can use them safely
     */
          
    /* Store the address of the saved context */
    movl  60(%esp), %ebx
    movl  %esp, (%ebx)
  
    /* This is the proper context switch ! We change the stack here */
    movl 64(%esp), %esp
  
    /* Restore the CPU context */
    popw %gs
    popw %fs
    popw %es
    popw %ds
    popw %ss
    addl $2,%esp
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    popl %esi
    popl %edi
    popl %ebp
    addl $4, %esp /* Ignore "error code" */
  
    /* This restores the eflags, the cs and the eip registers */
    iret /* equivalent to: popfl ; ret */
  
  resume_pc:
          // Same context as that when sos_cpu_kstate_switch got called
          // arg2= to_context    -- esp+8
          // arg1= from_context  -- esp+4
          // caller ip           -- esp
    ret
  
  
  
  /* ------------------------- */
  .globl sos_cpu_kstate_exit_to
  .type sos_cpu_kstate_exit_to, @function
  sos_cpu_kstate_exit_to:
          // arg3= reclaiming_arg  -- esp+12
          // arg2= reclaiming_func -- esp+8
          // arg1= to_context      -- esp+4
          // caller ip             -- esp
  
    /* Store the current SP in a temporary register */
    movl %esp, %eax
  
    /* This is the proper context switch ! We change the stack here */
    movl 4(%eax), %esp
  
    /* Call the reclaiming function (remember: the old frame address
       is stored in eax) */
    pushl 12(%eax)
    call  *8(%eax)
    addl  $4, %esp
  
    /* Restore the CPU context */
    popw %gs
    popw %fs
    popw %es
    popw %ds
    popw %ss
    addl $2,%esp
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    popl %esi
    popl %edi
    popl %ebp
    addl $4, %esp /* Ignore "error code" */
  
    /* This restores the eflags, the cs and the eip registers */
    iret /* equivalent to: popfl ; ret */
  
  
 

/tmp/sos-code-article5/hwcore/exception.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/exception.c (2005-01-11 09:32:39.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-article5/hwcore/exception.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/exception.h (2005-01-11 09:32:39.000000000 +0100 )
Line 27 
Line 27 
  
 #ifndef ASM_SOURCE #ifndef ASM_SOURCE
 #  include <sos/errno.h> #  include <sos/errno.h>
  #  include "cpu_context.h"
 #endif #endif
  
 /** /**
Line 69 
Line 70 
  
 #ifndef ASM_SOURCE #ifndef ASM_SOURCE
  
 typedef void (*sos_exception_handler_t)(int exception_number); typedef void (*sos_exception_handler_t)(int exception_number,
                                          const struct sos_cpu_kstate *cpu_kstate);
 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-article5/hwcore/exception_wrappers.S (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/exception_wrappers.S (2005-01-11 09:32:39.000000000 +0100 )
Line 86 
Line 86 
                 pushw %fs                 pushw %fs
                 pushw %gs                 pushw %gs
    
                 /* Call the handler with exception number as                 /*
                  * argument */                  * Call the handler with the exception number and the
                   * address of the stored CPU context as arguments
                   */
                  pushl %esp
                 leal  sos_exception_handler_array,%edi                 leal  sos_exception_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 addl  $4, %esp                 /* Unallocate the arguments passed to the handler */
                  addl  $8, %esp
                 /* Restore the context */                 /* Restore the context */
                 popw  %gs                 popw  %gs
Line 148 
Line 152 
                 pushw %fs                 pushw %fs
                 pushw %gs                 pushw %gs
  
                 /* Call the handler with exception number as                 /*
                  * argument */                  * Call the handler with the exception number and the
                   * address of the stored CPU context as arguments
                   */
                  pushl %esp
                 leal  sos_exception_handler_array,%edi                 leal  sos_exception_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 addl  $4, %esp                 /* Unallocate the arguments passed to the handler */
                  addl  $8, %esp
                 /* Restore the context */                 /* Restore the context */
                 popw  %gs                 popw  %gs
Line 178 
Line 186 
  
  
 /* Double fault handler not supported. We must define it since we /* Double fault handler not supported. We must define it since we
    define an entry for it in the sos_exception_wrapper_array. */    define an entry for it in the sos_exception_wrapper_array. It
     simply uses an alternate stack to display a message and stop the
     system. qemu won't handle it correctly (see comment in qemu's
     sources). */
  #define ALTERNATE_DOUBLE_FAULT_STACK_SIZE 512
 .p2align 2, 0x90 .p2align 2, 0x90
 sos_exception_wrapper_\id: sos_exception_wrapper_\id:
 .type sos_exception_wrapper_\id,@function .type sos_exception_wrapper_\id,@function
 1:      hlt 1:      cli /* Not necessary */
         jmp 1b /* Machine halting */         movl $double_fault_alternate_stack, %eax
          addl $ALTERNATE_DOUBLE_FAULT_STACK_SIZE, %eax
          movl %eax, %esp
          pushl $msg_double_fault_not_supported
          call sos_display_fatal_error ; jmp 1b /* Not necessary */
  
 /* Build the sos_irq_wrapper_array, shared with interrupt.c */ 
  msg_double_fault_not_supported:
          .string "exception_wrappers.S: Double fault detected ! NOT SUPPORTED yet. System Halted."
  
  /* Build the sos_irq_wrapper_array, shared with interrupt.c */
 .p2align 5, 0x0 .p2align 5, 0x0
 sos_exception_wrapper_array: sos_exception_wrapper_array:
         .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \         .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \
                  16,17,18,19,20,21,22,23,24,25,26,27,29,30,31                  16,17,18,19,20,21,22,23,24,25,26,27,29,30,31
           .long (sos_exception_wrapper_\id)           .long (sos_exception_wrapper_\id)
         .endr         .endr
  
  /* Alternate stack for double fault handler */
  .bss
  .p2align 2, 0x0
  .size double_fault_alternate_stack, ALTERNATE_DOUBLE_FAULT_STACK_SIZE
  double_fault_alternate_stack:
          .fill ALTERNATE_DOUBLE_FAULT_STACK_SIZE, 1, 0x0
  
 

/tmp/sos-code-article5/hwcore/gdt.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/gdt.c (2005-01-11 09:32:39.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-article5/hwcore/gdt.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/gdt.h (2005-01-11 09:32:39.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-article5/hwcore/i8259.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/i8259.c (2005-01-11 09:32:39.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-article5/hwcore/i8259.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/i8259.h (2005-01-11 09:32:39.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-article5/hwcore/idt.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/idt.c (2005-01-11 09:32:39.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-article5/hwcore/idt.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/idt.h (2005-01-11 09:32:39.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-article5/hwcore/irq.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/irq.c (2005-01-11 09:32:39.000000000 +0100 )
Line 21 
Line 21 
  
 #include "irq.h" #include "irq.h"
  
 /* array of IRQ wrappers, defined in irq_wrappers.S */ /** array of IRQ wrappers, defined in irq_wrappers.S */
  
 /* arrays of IRQ handlers, shared with irq_wrappers.S */ /** arrays of IRQ handlers, shared with irq_wrappers.S */
  
  /** Number of interrupt handlers that are currently executing */
  sos_ui32_t sos_irq_nested_level_counter;
  
 sos_ret_t sos_irq_setup(void) sos_ret_t sos_irq_subsystem_setup(void)
   return sos_i8259_setup();   sos_irq_nested_level_counter = 0;
    return sos_i8259_subsystem_setup();
  
  
Line 89 
Line 92 
   /* Expected to be atomic */   /* Expected to be atomic */
   return sos_irq_handler_array[irq_level];   return sos_irq_handler_array[irq_level];
 } }
  
  
  sos_ui32_t sos_irq_get_nested_level()
  {
    /* No need to disable interrupts here */
    return sos_irq_nested_level_counter;
  }
  
 

/tmp/sos-code-article5/hwcore/irq.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/irq.h (2005-01-11 09:32:39.000000000 +0100 )
Line 19 
Line 19 
 #ifndef _SOS_HWINTR_H_ #ifndef _SOS_HWINTR_H_
 #define _SOS_HWINTR_H_ #define _SOS_HWINTR_H_
  
  
 /** /**
  * @file irq.c  * @file irq.c
  *  *
  * Hardware interrupts routines management.  * Hardware interrupts routines management.
  */  */
  
  
 #include <sos/errno.h> #include <sos/errno.h>
  #include "cpu_context.h"
  
  
 #define sos_save_flags(flags) \ #define sos_save_flags(flags) \
   asm volatile("pushfl ; popl %0":"=g"(flags)::"memory")   asm volatile("pushfl ; popl %0":"=g"(flags)::"memory")
 #define sos_restore_flags(flags) \ #define sos_restore_flags(flags) \
   asm volatile("push %0; popfl"::"g"(flags):"memory")   asm volatile("push %0; popfl"::"g"(flags):"memory")
  
  
 #define sos_disable_IRQs(flags)    \ #define sos_disable_IRQs(flags)    \
   ({ sos_save_flags(flags); asm("cli\n"); })   ({ sos_save_flags(flags); asm("cli\n"); })
 #define sos_restore_IRQs(flags)    \ #define sos_restore_IRQs(flags)    \
   sos_restore_flags(flags)   sos_restore_flags(flags)
  
  
 /* Usual IRQ levels */ /* Usual IRQ levels */
 #define SOS_IRQ_TIMER         0 #define SOS_IRQ_TIMER         0
 #define SOS_IRQ_KEYBOARD      1 #define SOS_IRQ_KEYBOARD      1
Line 55 
Line 61 
 #define SOS_IRQ_HARDDISK      14 #define SOS_IRQ_HARDDISK      14
 #define SOS_IRQ_RESERVED_5    15 #define SOS_IRQ_RESERVED_5    15
  
 typedef void (*sos_irq_handler_t)(int irq_level); 
  /** Definition of an hardware IRQ handler */
  typedef void (*sos_irq_handler_t)(int irq_level,
                                    const struct sos_cpu_kstate *cpu_kstate);
  
 /** Setup the PIC */ /** Setup the PIC */
 sos_ret_t sos_irq_setup(void); sos_ret_t sos_irq_subsystem_setup(void);
  
 /** /**
  * If the routine is not NULL, the IDT is setup to call an IRQ  * If the routine is not NULL, the IDT is setup to call an IRQ
Line 71 
Line 82 
  
 sos_irq_handler_t sos_irq_get_routine(int irq_level); sos_irq_handler_t sos_irq_get_routine(int irq_level);
  
  
  /**
   * Tell how many nested IRQ handler have been fired
   */
  sos_ui32_t sos_irq_get_nested_level();
  
  
  /**
   * Return TRUE when we are currently executing in interrupt context
   */
  #define sos_servicing_irq() \
    (sos_irq_get_nested_level() > 0)
  
  
 #endif /* _SOS_HWINTR_H_ */ #endif /* _SOS_HWINTR_H_ */
  
 

/tmp/sos-code-article5/hwcore/irq_wrappers.S (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/irq_wrappers.S (2005-01-11 09:32:39.000000000 +0100 )
Line 22 
Line 22 
  
 .text .text
  
 /* The address of the table of handlers (defined in irq.c) */ /** The address of the table of handlers (defined in irq.c) */
  
 /* The address of the table of wrappers (defined below, and shared /** The address of the table of wrappers (defined below, and shared
 .globl sos_irq_wrapper_array .globl sos_irq_wrapper_array
  
  /** The variable holding the nested level of the IRQ handlers */
  .extern sos_irq_nested_level_counter
  
 /* These pre-handlers are for IRQ (Master PIC) */ /* These pre-handlers are for IRQ (Master PIC) */
 .irp id, 0,1,2,3,4,5,6,7 .irp id, 0,1,2,3,4,5,6,7
Line 62 
Line 64 
                 pushw %fs                 pushw %fs
                 pushw %gs                 pushw %gs
  
                  /*
                   * Increment IRQ nested level
                   */
                  incl sos_irq_nested_level_counter
  
                 /* Send EOI to PIC. See Intel 8259 datasheet                 /* Send EOI to PIC. See Intel 8259 datasheet
                    available on Kos website */                            available on Kos website */        
                 movb  $0x20, %al                 movb  $0x20, %al
                 outb  %al, $0x20                 outb  %al, $0x20
                  
                 /*                 /*
                  * Call the handler with IRQ number as argument                  * Call the handler with the IRQ number and the
                   * address of the stored CPU context as arguments
                  pushl %esp
                 pushl $\id                 pushl $\id
                 leal  sos_irq_handler_array,%edi                 leal  sos_irq_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 addl  $4, %esp                 /* Unallocate the arguments passed to the handler */
                  addl  $8, %esp
          
                  /*
                   * Decrement IRQ nested level
                   */
                  cli  /* Just in case we messed up everything in the handler */
                  subl $1, sos_irq_nested_level_counter
  
                  /* sos_irq_nested_level_counter went below 0 ?! */
                  jnc 2f
          
          1:      /* Yes:        Print fatal error message */
                  pushl $msg_nested_level_overflow
                  call sos_display_fatal_error
                  addl $4, %esp ; jmp 1b
                  /* Never returns */
  
          2:        /* No:         all right ! */
                 /* Restore the context */                 /* Restore the context */
                 popw  %gs                 popw  %gs
Line 129 
Line 156 
                 pushw %fs                 pushw %fs
                 pushw %gs                 pushw %gs
  
                  /*
                   * Increment IRQ nested level
                   */
                  incl sos_irq_nested_level_counter
  
                 /* Send EOI to PIC. See Intel 8259 datasheet                 /* Send EOI to PIC. See Intel 8259 datasheet
                    available on Kos website */                            available on Kos website */        
                 movb  $0x20, %al                 movb  $0x20, %al
Line 136 
Line 168 
                 outb  %al, $0x20                 outb  %al, $0x20
  
                 /*                 /*
                  * Call the handler with IRQ number as argument                  * Call the handler with the IRQ number and the
                   * address of the stored CPU context as arguments
                  pushl %esp
                 pushl $\id                 pushl $\id
                 leal  sos_irq_handler_array,%edi                 leal  sos_irq_handler_array,%edi
                 call  *\id*4(%edi)                 call  *\id*4(%edi)
                 addl  $4, %esp                 /* Unallocate the arguments passed to the handler */
                  addl  $8, %esp
  
                  /*
                   * Decrement IRQ nested level
                   */
                  cli  /* Just in case we messed up everything in the handler */
                  subl $1, sos_irq_nested_level_counter
  
                  /* sos_irq_nested_level_counter went below 0 ?! */
                  jnc 2f
                  
          1:      /* Yes:        Print fatal error message */
                  pushl $msg_nested_level_overflow
                  call sos_display_fatal_error
                  addl $4, %esp ; jmp 1b
                  /* Never returns */
  
          2:        /* No:         all right ! */
                 /* Restore the context */                 /* Restore the context */
                 popw  %gs                 popw  %gs
Line 164 
Line 216 
                 iret                 iret
         .endr         .endr
  
 /* Build the sos_irq_wrapper_array, shared with irq.c */ 
  msg_nested_level_overflow:
          .string "irq_wrappers.S: IRQ Nested level overflow ! System halted."
  
  /* Build the sos_irq_wrapper_array, shared with irq.c */
 .p2align 5, 0x0 .p2align 5, 0x0
 sos_irq_wrapper_array: sos_irq_wrapper_array:
         .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15         .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
  
 

/tmp/sos-code-article5/hwcore/paging.c (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/paging.c (2005-01-11 09:32:39.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;  
Line 262 
Line 262 
 sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr,
                          sos_vaddr_t vpage_vaddr,                          sos_vaddr_t vpage_vaddr,
                          sos_bool_t is_user_page,                          sos_bool_t is_user_page,
                          int flags)                          sos_ui32_t flags)
   /* Get the page directory entry and table entry index for this   /* Get the page directory entry and table entry index for this
      address */      address */
  
 

/tmp/sos-code-article5/hwcore/paging.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/paging.h (2005-01-11 09:32:39.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
Line 89 
Line 89 
 sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr,
                          sos_vaddr_t vpage_vaddr,                          sos_vaddr_t vpage_vaddr,
                          sos_bool_t is_user_page,                          sos_bool_t is_user_page,
                          int flags);                          sos_ui32_t flags);
 /** /**
  * Undo the mapping from vaddr to the underlying physical page (if any)  * Undo the mapping from vaddr to the underlying physical page (if any)
  
 

/tmp/sos-code-article5/hwcore/segment.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/hwcore/segment.h (2005-01-11 09:32:39.000000000 +0100 )
Line 37 
Line 37 
  *  *
  * @see gdt.h  * @see gdt.h
  */  */
 #define SOS_SEG_NULL  0 /* NULL segment, unused by the procesor */ #define SOS_SEG_NULL       0 /* NULL segment, unused by the procesor */
 #define SOS_SEG_KCODE 1 /* Kernel code segment */ #define SOS_SEG_KCODE      1 /* Kernel code segment */
 #define SOS_SEG_KDATA 2 /* Kernel data segment */ #define SOS_SEG_KDATA      2 /* Kernel data segment */
  
 /** /**
  
 

/tmp/sos-code-article5/sos/assert.c (1970-01-01 01:00:00.000000000 +0100 )
../sos-code-article6/sos/assert.c (2005-01-11 09:32:40.000000000 +0100 )
(New file) 
Line 1 
  /* Copyright (C) 2004  The KOS Team
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  
  #include <sos/klibc.h>
  #include <drivers/bochs.h>
  #include <drivers/x86_videomem.h>
  
  #include "assert.h"
  
  void sos_display_fatal_error(const char *format, /* args */...)
  {
    char buff[256];
    va_list ap;
    
    asm("cli\n"); /* disable interrupts -- x86 only */ \
  
    va_start(ap, format);
    vsnprintf(buff, sizeof(buff), format, ap);
    va_end(ap);
  
    sos_bochs_putstring(buff); sos_bochs_putstring("\n");
    sos_x86_videomem_putstring(24, 0,
                               SOS_X86_VIDEO_BG_BLACK
                               | SOS_X86_VIDEO_FG_LTRED , buff);
  
    /* Infinite loop: processor halted */
    for ( ; ; )
      asm("hlt\n");
  }
  
 

/tmp/sos-code-article5/sos/assert.h (2004-09-28 09:19:56.000000000 +0200 )
../sos-code-article6/sos/assert.h (2005-01-11 09:32:39.000000000 +0100 )
Line 18 
Line 18 
 #ifndef _SOS_ASSERT_H_ #ifndef _SOS_ASSERT_H_
 #define _SOS_ASSERT_H_ #define _SOS_ASSERT_H_
  
 #include <drivers/bochs.h> 
 #include <drivers/x86_videomem.h> void sos_display_fatal_error(const char *format, /* args */...)
       __attribute__ ((format (printf, 1, 2), noreturn));
  
 /** /**
  * If the expr is FALSE, print a message and halt the machine  * If the expr is FALSE, print a message and halt the machine
Line 27 
Line 29 
 #define SOS_ASSERT_FATAL(expr) \ #define SOS_ASSERT_FATAL(expr) \
    ({ \    ({ \
      int __res=(int)(expr); \      int __res=(int)(expr); \
      if (! __res) { \      if (! __res) \
        asm("cli\n"); /* disable interrupts -- x86 only */ \        sos_display_fatal_error("%s@%s:%d Assertion " # expr " failed", \
        sos_bochs_printf("%s@%s:%d Assertion " # expr " failed\n", \ 
                         __PRETTY_FUNCTION__, __FILE__, __LINE__); \ 
        sos_x86_videomem_printf(24, 0, 12, \ 
                                "%s@%s:%d Assertion " # expr " failed", \ 
        for (;;) asm("hlt;") ; /* Infinite loop, ie simple system halt */ \ 
      } \ 
  
  
  #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-article5/sos/klibc.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/klibc.c (2005-01-11 09:32:39.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)
Line 213 
Line 247 
               break;               break;
             }             }
  
            case 'p':
              PUTCHAR('0');
              PUTCHAR('x');
           case 'x':           case 'x':
             {             {
               unsigned int hexa = va_arg(ap,int);               unsigned int hexa = va_arg(ap,int);
  
 

/tmp/sos-code-article5/sos/klibc.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/klibc.h (2005-01-11 09:32:39.000000000 +0100 )
Line 24 
Line 24 
  *  *
  * Basic libc-style support for common useful functions (string.h,  * Basic libc-style support for common useful functions (string.h,
  * stdarg.h), some with slight non-standard behavior (see comments).  * stdarg.h), some with slight non-standard behavior (see comments).
   *
   * Most of the prototypes of these functions are borrowed from
   * FreeBSD, but their implementation (in klibc.c) come either from Kos
   * (GPL v2) or from David Decotigny (SOS).
  */  */
  
 #include <sos/types.h> #include <sos/types.h>
Line 81 
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-article5/sos/kmalloc.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmalloc.c (2005-01-11 09:32:39.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-article5/sos/kmalloc.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmalloc.h (2005-01-11 09:32:39.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
Line 43 
Line 43 
 /** /**
  * Allocate a kernel object of the given size in the most suited slab  * Allocate a kernel object of the given size in the most suited slab
  * cache if size can be handled by one of the pre-allocated caches, or  * cache if size can be handled by one of the pre-allocated caches, or
  * using directly the range allocator otherwise.  * using directly the range allocator otherwise. The object will
   * allways be mapped in physical memory (ie implies
   * SOS_KSLAB_CREATE_MAP and SOS_KMEM_VMM_MAP).
  * @param size  The size of the object  * @param size  The size of the object
  * @param flags The allocation flags (SOS_KMALLOC_* flags)  * @param flags The allocation flags (SOS_KMALLOC_* flags)
  
 

/tmp/sos-code-article5/sos/kmem_slab.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmem_slab.c (2005-01-11 09:32:39.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 469 
Line 469 
         = sos_physmem_ref_physpage_new(FALSE);         = sos_physmem_ref_physpage_new(FALSE);
       SOS_ASSERT_FATAL(ppage_paddr != (sos_paddr_t)NULL);       SOS_ASSERT_FATAL(ppage_paddr != (sos_paddr_t)NULL);
  
       retval =sos_paging_map(ppage_paddr, vaddr,       retval = sos_paging_map(ppage_paddr, vaddr,
                              FALSE,                               FALSE,
                              SOS_VM_MAP_ATOMIC                               SOS_VM_MAP_ATOMIC
                              | SOS_VM_MAP_PROT_READ                               | SOS_VM_MAP_PROT_READ
                              | SOS_VM_MAP_PROT_WRITE);                               | SOS_VM_MAP_PROT_WRITE);
  
       retval = sos_physmem_unref_physpage(ppage_paddr);       retval = sos_physmem_unref_physpage(ppage_paddr);
       SOS_ASSERT_FATAL(retval == SOS_OK);       SOS_ASSERT_FATAL(retval == FALSE);
  
   /* Create the cache of caches */   /* Create the cache of caches */
Line 515 
Line 515 
       SOS_ASSERT_FATAL(retval == SOS_OK);       SOS_ASSERT_FATAL(retval == SOS_OK);
  
       retval = sos_physmem_unref_physpage(ppage_paddr);       retval = sos_physmem_unref_physpage(ppage_paddr);
       SOS_ASSERT_FATAL(retval == SOS_OK);       SOS_ASSERT_FATAL(retval == FALSE);
  
   /* Create the cache of ranges */   /* Create the cache of ranges */
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-article5/sos/kmem_slab.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmem_slab.h (2005-01-11 09:32:39.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-article5/sos/kmem_vmm.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmem_vmm.c (2005-01-11 09:32:39.000000000 +0100 )
Line 120 
Line 120 
   struct sos_kmem_range *range;   struct sos_kmem_range *range;
  
   /* First: try to retrieve the physical page mapped at this address */   /* First: try to retrieve the physical page mapped at this address */
   sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr);   sos_paddr_t ppage_paddr = SOS_PAGE_ALIGN_INF(sos_paging_get_paddr(vaddr));
   if (! ppage_paddr)   if (ppage_paddr)
       range = sos_physmem_get_kmem_range(ppage_paddr);       range = sos_physmem_get_kmem_range(ppage_paddr);
  
       /* If a page is mapped at this address, it is EXPECTED that it       /* If a page is mapped at this address, it is EXPECTED that it
          is really associated with a range */          is really associated with a range */
       SOS_ASSERT_FATAL(range != NULL);       SOS_ASSERT_FATAL(range != NULL);
Line 138 
Line 139 
       /* Not found */       /* Not found */
       if (! range)       if (! range)
         return NULL;         return NULL;
  
        /* vaddr not covered by this range */
        if ( (vaddr < range->base_vaddr)
             || (vaddr >= (range->base_vaddr + range->nb_pages*SOS_PAGE_SIZE)) )
          return NULL;
     }     }
  
   return range;   return range;
Line 152 
Line 158 
 static struct sos_kmem_range * static struct sos_kmem_range *
 create_range(sos_bool_t  is_free, create_range(sos_bool_t  is_free,
              sos_vaddr_t base_vaddr,              sos_vaddr_t base_vaddr,
              sos_vaddr_t top_addr,              sos_vaddr_t top_vaddr,
 { {
   struct sos_kmem_range *range;   struct sos_kmem_range *range;
  
    SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(base_vaddr));
    SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(top_vaddr));
  
    if ((top_vaddr - base_vaddr) < SOS_PAGE_SIZE)
      return NULL;
  
   range = (struct sos_kmem_range*)sos_kmem_cache_alloc(kmem_range_cache,   range = (struct sos_kmem_range*)sos_kmem_cache_alloc(kmem_range_cache,
                                                        SOS_KSLAB_ALLOC_ATOMIC);                                                        SOS_KSLAB_ALLOC_ATOMIC);
   SOS_ASSERT_FATAL(range != NULL);   SOS_ASSERT_FATAL(range != NULL);
  
   range->base_vaddr = base_vaddr;   range->base_vaddr = base_vaddr;
   range->nb_pages   = (top_addr - base_vaddr) / SOS_PAGE_SIZE;   range->nb_pages   = (top_vaddr - base_vaddr) / SOS_PAGE_SIZE;
   if (is_free)   if (is_free)
     {     {
Line 177 
Line 190 
  
       /* Ok, set the range owner for the pages in this page */       /* Ok, set the range owner for the pages in this page */
       for (vaddr = base_vaddr ;       for (vaddr = base_vaddr ;
            vaddr < top_addr ;            vaddr < top_vaddr ;
       {       {
         sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr);         sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr);
Line 190 
Line 203 
 } }
  
  
 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,
                               sos_vaddr_t kernel_core_top,
                               sos_vaddr_t bootstrap_stack_bottom_vaddr,
                               sos_vaddr_t bootstrap_stack_top_vaddr)
   struct sos_kslab *first_struct_slab_of_caches,   struct sos_kslab *first_struct_slab_of_caches,
     *first_struct_slab_of_ranges;     *first_struct_slab_of_ranges;
Line 206 
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 235 
Line 251 
                SOS_PAGE_ALIGN_INF(kernel_core_base),                SOS_PAGE_ALIGN_INF(kernel_core_base),
                NULL);                NULL);
      
   /* Mark virtual addresses in Kernel code/data as NOT FREE */   /* Mark virtual addresses in Kernel code/data up to the bootstrap stack
       as NOT FREE */
                SOS_PAGE_ALIGN_INF(kernel_core_base),                SOS_PAGE_ALIGN_INF(kernel_core_base),
                 bootstrap_stack_bottom_vaddr,
                 NULL);
  
    /* Mark virtual addresses in the bootstrap stack as NOT FREE too,
       but in another vmm region in order to be un-allocated later */
    create_range(FALSE,
                 bootstrap_stack_bottom_vaddr,
                 bootstrap_stack_top_vaddr,
                 NULL);
  
    /* Mark the remaining virtual addresses in Kernel code/data after
       the bootstrap stack as NOT FREE */
    create_range(FALSE,
                 bootstrap_stack_top_vaddr,
                SOS_PAGE_ALIGN_SUP(kernel_core_top),                SOS_PAGE_ALIGN_SUP(kernel_core_top),
                NULL);                NULL);
  
Line 276 
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;
 } }
Line 387 
Line 418 
           sos_physmem_set_kmem_range(ppage_paddr, new_range);           sos_physmem_set_kmem_range(ppage_paddr, new_range);
         }         }
     }     }
    /* ... Otherwise: Demand Paging will do the job */
   /* Otherwise we need a correct page fault handler to support 
      deferred mapping (aka demand paging) of ranges */ 
   else 
     SOS_ASSERT_FATAL(! "No demand paging yet"); 
   if (range_start)   if (range_start)
     *range_start = new_range->base_vaddr;     *range_start = new_range->base_vaddr;
Line 400 
Line 427 
 } }
  
  
 sos_vaddr_t sos_kmem_vmm_del_range(struct sos_kmem_range *range) sos_ret_t sos_kmem_vmm_del_range(struct sos_kmem_range *range)
   int i;   int i;
   struct sos_kmem_range *ranges_to_free;   struct sos_kmem_range *ranges_to_free;
Line 534 
Line 561 
 } }
  
  
 sos_vaddr_t sos_kmem_vmm_free(sos_vaddr_t vaddr) sos_ret_t sos_kmem_vmm_free(sos_vaddr_t vaddr)
   struct sos_kmem_range *range = lookup_range(vaddr);   struct sos_kmem_range *range = lookup_range(vaddr);
  
Line 570 
Line 597 
   return range->slab;   return range->slab;
 } }
  
  
  sos_bool_t sos_kmem_vmm_is_valid_vaddr(sos_vaddr_t vaddr)
  {
    struct sos_kmem_range *range = lookup_range(vaddr);
    return (range != NULL);
  }
  
 

/tmp/sos-code-article5/sos/kmem_vmm.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/kmem_vmm.h (2005-01-11 09:32:39.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 kernel_core_top_vaddr,
                               sos_vaddr_t bootstrap_stack_bottom_vaddr,
                               sos_vaddr_t bootstrap_stack_top_vaddr);
  
 /* /*
Line 66 
Line 69 
 struct sos_kmem_range *sos_kmem_vmm_new_range(sos_size_t  nb_pages, struct sos_kmem_range *sos_kmem_vmm_new_range(sos_size_t  nb_pages,
                                               sos_ui32_t  flags,                                               sos_ui32_t  flags,
                                               sos_vaddr_t *range_base_vaddr);                                               sos_vaddr_t *range_base_vaddr);
 sos_vaddr_t sos_kmem_vmm_del_range(struct sos_kmem_range *range); sos_ret_t sos_kmem_vmm_del_range(struct sos_kmem_range *range);
  
 /** /**
Line 82 
Line 85 
  * the kernel/bios WILL be "deallocated". But if you really want to do  * the kernel/bios WILL be "deallocated". But if you really want to do
  * this, well..., do expect some "surprises" ;)  * this, well..., do expect some "surprises" ;)
  */  */
 sos_vaddr_t sos_kmem_vmm_free(sos_vaddr_t vaddr); sos_ret_t sos_kmem_vmm_free(sos_vaddr_t vaddr);
  
  
  /**
   * @return TRUE when vaddr is covered by any (used) kernel range
   */
  sos_bool_t sos_kmem_vmm_is_valid_vaddr(sos_vaddr_t vaddr);
  
 /* ***************************** /* *****************************
Line 95 
Line 104 
                                 struct sos_kslab *slab);                                 struct sos_kslab *slab);
  
 /** /**
  * Retrieve the slab associated with the  * Retrieve the (used) slab associated with the range covering vaddr.
  * range covering vaddr. 
  * @return NULL if the range is not associated with a KMEM range  * @return NULL if the range is not associated with a KMEM range
  */  */
  
 

/tmp/sos-code-article5/sos/macros.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/macros.h (2005-01-11 09:32:39.000000000 +0100 )
Line 27 
Line 27 
   ({ unsigned int __bnd=(boundary); \   ({ unsigned int __bnd=(boundary); \
      (((((unsigned)(val))-1) & (~(__bnd - 1))) + __bnd); })      (((((unsigned)(val))-1) & (~(__bnd - 1))) + __bnd); })
  
  /** Check whether val is aligned on a boundary (MUST be a power of 2) */
  #define SOS_IS_ALIGNED(val,boundary) \
    ( 0 == (((unsigned)(val)) & ((boundary)-1)) )
  
 /** /**
  * @return TRUE if val is a power of 2.  * @return TRUE if val is a power of 2.
  * @note val is evaluated multiple times  * @note val is evaluated multiple times
  
 

/tmp/sos-code-article5/sos/main.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/main.c (2005-01-11 09:32:39.000000000 +0100 )
Line 37 
Line 37 
  
 /* Helper function to display each bits of a 32bits integer on the /* Helper function to display each bits of a 32bits integer on the
    screen as dark or light carrets */    screen as dark or light carrets */
 static void display_bits(unsigned char row, unsigned char col, void display_bits(unsigned char row, unsigned char col,
                          unsigned char attribute,                   unsigned char attribute,
                          sos_ui32_t integer)                   sos_ui32_t integer)
   int i;   int i;
   /* Scan each bit of the integer, MSb first */   /* Scan each bit of the integer, MSb first */
Line 57 
Line 57 
  
  
 /* Clock IRQ handler */ /* Clock IRQ handler */
 static void clk_it(int intid) static void clk_it(int intid,
                     const struct sos_cpu_kstate *cpu_kstate)
   static sos_ui32_t clock_count = 0;   static sos_ui32_t clock_count = 0;
  
Line 65 
Line 66 
                SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE,                SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE,
                clock_count);                clock_count);
   clock_count++;   clock_count++;
  
 struct digit 
  
  /* ======================================================================
   * Page fault exception handling
   */
  
  /* Helper function to dump a backtrace on bochs and/or the console */
  static void dump_backtrace(const struct sos_cpu_kstate *cpu_kstate,
                             sos_vaddr_t stack_bottom,
                             sos_size_t  stack_size,
                             sos_bool_t on_console,
                             sos_bool_t on_bochs)
   struct digit *prev, *next;   static void backtracer(sos_vaddr_t PC,
   char value;                          sos_vaddr_t params,
 };                          sos_ui32_t depth,
                           void *custom_arg)
      {
        sos_ui32_t invalid = 0xffffffff, *arg1, *arg2, *arg3, *arg4;
 /* Representation of a big (positive) integer: Most Significant Digit       /* Get the address of the first 3 arguments from the
    (MSD) is the HEAD of the list. Least Significant Digit (LSD) is the          frame. Among these arguments, 0, 1, 2, 3 arguments might be
    TAIL of the list */          meaningful (depending on how many arguments the function may
 typedef struct digit * big_number_t;          take). */
        arg1 = (sos_ui32_t*)params;
        arg2 = (sos_ui32_t*)(params+4);
        arg3 = (sos_ui32_t*)(params+8);
        arg4 = (sos_ui32_t*)(params+12);
  
        /* Make sure the addresses of these arguments fit inside the
           stack boundaries */
  #define INTERVAL_OK(b,v,u) ( ((b) <= (sos_vaddr_t)(v)) \
                               && ((sos_vaddr_t)(v) < (u)) )
        if (!INTERVAL_OK(stack_bottom, arg1, stack_bottom + stack_size))
          arg1 = &invalid;
        if (!INTERVAL_OK(stack_bottom, arg2, stack_bottom + stack_size))
          arg2 = &invalid;
        if (!INTERVAL_OK(stack_bottom, arg3, stack_bottom + stack_size))
          arg3 = &invalid;
        if (!INTERVAL_OK(stack_bottom, arg4, stack_bottom + stack_size))
          arg4 = &invalid;
  
        /* Print the function context for this frame */
        if (on_bochs)
          sos_bochs_printf("[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x\n",
                           (unsigned)depth, (unsigned)PC,
                           (unsigned)*arg1, (unsigned)*arg2,
                           (unsigned)*arg3);
  
        if (on_console)
          sos_x86_videomem_printf(23-depth, 3,
                                  SOS_X86_VIDEO_BG_BLUE
                                    | SOS_X86_VIDEO_FG_LTGREEN,
                                  "[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x arg4=0x%x",
                                  (unsigned)depth, PC,
                                  (unsigned)*arg1, (unsigned)*arg2,
                                  (unsigned)*arg3, (unsigned)*arg4);
        
      }
  
    sos_backtrace(cpu_kstate, 15, stack_bottom, stack_size, backtracer, NULL);
  }
  
 /* Add a new digit after the LSD */ /* Page fault exception handler with demand paging for the kernel */
 void bn_push_lsd(big_number_t * bn, char value) static void pgflt_ex(int intid, const struct sos_cpu_kstate *ctxt)
   struct digit *d;   static sos_ui32_t demand_paging_count = 0;
   d = (struct digit*) sos_kmalloc(sizeof(struct digit), 0);   sos_vaddr_t faulting_vaddr = sos_cpu_kstate_get_EX_faulting_vaddr(ctxt);
   SOS_ASSERT_FATAL(d != NULL);   sos_paddr_t ppage_paddr;
   d->value = value; 
   list_add_tail(*bn, d);   /* Check if address is covered by any VMM range */
    if (! sos_kmem_vmm_is_valid_vaddr(faulting_vaddr))
      {
        /* No: The page fault is out of any kernel virtual region. For
           the moment, we don't handle this. */
        dump_backtrace(ctxt,
                       bootstrap_stack_bottom,
                       bootstrap_stack_size,
                       TRUE, TRUE);
        sos_display_fatal_error("Unresolved page Fault on access to address 0x%x (info=%x)!",
                                (unsigned)faulting_vaddr,
                                (unsigned)sos_cpu_kstate_get_EX_info(ctxt));
        SOS_ASSERT_FATAL(! "Got page fault (note: demand paging is disabled)");
      }
  
  
    /*
     * Demand paging
     */
   
    /* Update the number of demand paging requests handled */
    demand_paging_count ++;
    display_bits(0, 0,
                 SOS_X86_VIDEO_FG_LTRED | SOS_X86_VIDEO_BG_BLUE,
                 demand_paging_count);
  
    /* Allocate a new page for the virtual address */
    ppage_paddr = sos_physmem_ref_physpage_new(FALSE);
    if (! ppage_paddr)
      SOS_ASSERT_FATAL(! "TODO: implement swap. (Out of mem in demand paging because no swap for kernel yet !)");
    SOS_ASSERT_FATAL(SOS_OK == sos_paging_map(ppage_paddr,
                                              SOS_PAGE_ALIGN_INF(faulting_vaddr),
                                              FALSE,
                                              SOS_VM_MAP_PROT_READ
                                              | SOS_VM_MAP_PROT_WRITE
                                              | SOS_VM_MAP_ATOMIC));
    sos_physmem_unref_physpage(ppage_paddr);
  
    /* Ok, we can now return to interrupted context */
  
  
 /* Add a new digit before the MSD */ 
 void bn_push_msd(big_number_t * bn, char value) /* ======================================================================
   * 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)
   struct digit *d;   sos_kfree(stack_vaddr);
   d = (struct digit*) sos_kmalloc(sizeof(struct digit), 0); 
   SOS_ASSERT_FATAL(d != NULL); 
   d->value = value; 
   list_add_head(*bn, d); 
  
  
 /* Construct a big integer from a (machine) integer */ static void exit_hello12(sos_vaddr_t stack_vaddr)
 big_number_t bn_new(unsigned long int i) 
   big_number_t retval;   sos_cpu_kstate_exit_to(ctxt_main,
                           (sos_cpu_kstate_function_arg1_t*) reclaim_stack,
                           stack_vaddr);
  }
   list_init(retval); 
   do static void hello1 (char *str)
  {
    for ( ; *str != '\n' ; str++)
       bn_push_msd(&retval, i%10);       sos_bochs_printf("hello1: %c\n", *str);
       i /= 10;       sos_cpu_kstate_switch(& ctxt_hello1, ctxt_hello2);
   while (i != 0); 
   return retval;   /* 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); */
  
  
 /* Create a new big integer from another big integer */ static void hello2 (char *str)
 big_number_t bn_copy(const big_number_t bn) 
   big_number_t retval;   for ( ; *str != '\n' ; str++)
   int nb_elts; 
   struct digit *d; 
  
   list_init(retval); 
   list_foreach(bn, d, nb_elts) 
       bn_push_lsd(&retval, d->value);       sos_bochs_printf("hello2: %c\n", *str);
        sos_cpu_kstate_switch(& ctxt_hello2, ctxt_hello1);
  
   return retval;   /* 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); */
  
  
 /* Free the memory used by a big integer */ void print_hello_world ()
 void bn_del(big_number_t * bn) 
   struct digit *d; #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);
   list_collapse(*bn, d)   /* The first coroutine to reach the '\n' switched back to us */
     {   sos_bochs_printf("Back in main !\n");
       sos_kfree((sos_vaddr_t)d); 
     } 
  
  
 /* Shift left a big integer: bn := bn*10^shift */ /* ======================================================================
 void bn_shift(big_number_t *bn, int shift)  * 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)
   for ( ; shift > 0 ; shift --)   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++)
       bn_push_lsd(bn, 0);       /* 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)");
 } }
  
  
 /* Dump the big integer in bochs */ 
 void bn_print_bochs(const big_number_t bn) /* ======================================================================
   * Shows how the backtrace stuff works
   */
  
  /* Recursive function. Print the backtrace from the innermost function */
  static void test_backtrace(int i, int magic, sos_vaddr_t stack_bottom,
                             sos_size_t  stack_size)
   int nb_elts;   if (i <= 0)
   const struct digit *d;     {
        /* The page fault exception handler will print the backtrace of
           this function, because address 0x42 is not mapped */
        *((char*)0x42) = 12;
   if (list_is_empty(bn))       /* More direct variant: */
     sos_bochs_printf("0");       /* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */
      }
     list_foreach(bn, d, nb_elts)     test_backtrace(i-1, magic, stack_bottom, stack_size);
       sos_bochs_printf("%d", d->value); 
  
 /* Dump the big integer on the console */ 
 void bn_print_console(unsigned char row, unsigned char col, /* ======================================================================
                       unsigned char attribute,  * Parsing of Mathematical expressions
                       const big_number_t bn,  *
                       int nb_decimals)  * 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;
  
  
  /*
   * Default exit/reclaim functions: return control to the "sos_main()"
   * context
   */
  static void reclaim(int unused)
   if (list_is_empty(bn)) }
     sos_x86_videomem_printf(row, col, attribute, "0"); static void func_exit(sos_ui32_t unused)
   else {
     {   sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0);
       int nb_elts; }
       const struct digit *d; 
       unsigned char x = col; 
       list_foreach(bn, d, nb_elts) 
         { 
           if (nb_elts == 0) 
             { 
               sos_x86_videomem_printf(row, x, attribute, "%d.", d->value); 
               x += 2; 
             } 
           else if (nb_elts < nb_decimals) 
             { 
               sos_x86_videomem_printf(row, x, attribute, "%d", d->value); 
               x ++; 
             } 
         } 
       sos_x86_videomem_printf(row, x, attribute, " . 10^{%d}  ", nb_elts-1); /*
   * 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)
  {
    for ( ; str && (*str != '\0') ; str++)
      {
        data_reader_to_lexer = *str;
        sos_cpu_kstate_switch(& st_reader, st_lexer);
  
    data_reader_to_lexer = '\0';
    sos_cpu_kstate_switch(& st_reader, st_lexer);
 } }
  
  
 /* Result is the addition of 2 big integers */ /*
 big_number_t bn_add (const big_number_t bn1, const big_number_t bn2)  * The Lexer coroutine and associated types/variables. This coroutine
   * 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)
   big_number_t retval;   char c;
   const struct digit *d1, *d2;   enum { GOT_SPACE, GOT_NUM, GOT_OP, GOT_STR,
   sos_bool_t  bn1_end = FALSE, bn2_end = FALSE;          GOT_OPENPAR, GOT_CLOSEPAR } got_what, got_what_before;
   char carry = 0; 
   list_init(retval);   data_lexer_to_parser.number = 0;
   d1 = list_get_tail(bn1);   got_what_before = GOT_SPACE;
   bn1_end = list_is_empty(bn1); 
   d2 = list_get_tail(bn2); 
   bn2_end = list_is_empty(bn2); 
     {     {
       if (! bn1_end)       /* Consume one character from the reader */
         carry += d1->value;       sos_cpu_kstate_switch(& st_lexer, st_reader);
       if (! bn2_end)       c = data_reader_to_lexer;
         carry += d2->value; 
        /* Classify the consumed character */
       bn_push_msd(&retval, carry % 10);       if ( (c >= '0') && (c <= '9') )
       carry  /= 10;         got_what = GOT_NUM;
        else if ( (c == '+') || (c == '-') || (c == '*') || (c == '/') )
       if (! bn1_end)         got_what = GOT_OP;
         d1 = d1->prev;       else if ( ( (c >= 'a') && (c <= 'z') )
       if (! bn2_end)                 || ( (c >= 'A') && (c <= 'Z') ) )
         d2 = d2->prev;         got_what = GOT_STR;
       if (d1 == list_get_tail(bn1))       else if (c == '(')
         bn1_end = TRUE;         got_what = GOT_OPENPAR;
       if (d2 == list_get_tail(bn2))       else if (c == ')')
         bn2_end = TRUE;         got_what = GOT_CLOSEPAR;
     }       else
   while (!bn1_end || !bn2_end);         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
               has been recognized */
            if ( (got_what_before != GOT_SPACE) )
              sos_cpu_kstate_switch(& st_lexer, st_parser);
   if (carry > 0)           data_lexer_to_parser.number = 0;
     {         }
       bn_push_msd(&retval, carry); 
        /* Update the token being currently recognized */
        if (got_what == GOT_OP)
          {
            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;
            data_lexer_to_parser.number *= 10;
            data_lexer_to_parser.number += (c - '0');
          }
        else if (got_what == GOT_STR)
          {
            char to_cat[] = { c, '\0' };
            data_lexer_to_parser.type = LEX_IS_VAR;
            strzcat(data_lexer_to_parser.var, to_cat, STR_VAR_MAXLEN);
          }
        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;
    while (c != '\0');
  
   return retval;   /* 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);
  
  
 /* Result is the multiplication of a big integer by a single digit */ /*
 big_number_t bn_muli (const big_number_t bn, char digit)  * The Parser coroutine and associated types/variables
   */
  struct syntax_node
   big_number_t retval;   enum { YY_IS_BINOP, YY_IS_UNAROP, YY_IS_NUM, YY_IS_VAR } type;
   int nb_elts;   union
   char   carry = 0;   {
   const struct digit *d;     int  number;
      char var[STR_VAR_MAXLEN];
      struct
      {
        char op;
        struct syntax_node *parm_left, *parm_right;
      } binop;
      struct
      {
        char op;
        struct syntax_node *parm;
      } unarop;
    };
  };
   list_init(retval); static void func_parser(struct syntax_node ** syntax_tree)
   list_foreach_backward(bn, d, nb_elts) {
    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 */
    static struct syntax_node *alloc_node_num(int val)
      {
        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)
       carry += d->value * digit;       struct syntax_node *n
       bn_push_msd(&retval, carry % 10);         = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0);
       carry /= 10;       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;
  
   if (carry > 0)   /* Raise an exception: transfer control back to main context,
       without unrolling the whole recursion */
    static void parser_exception(const char *str)
       bn_push_msd(&retval, carry);       sos_bochs_printf("Parser exception: %s\n", str);
        sos_cpu_kstate_switch(& st_parser, st_main);
  
   return retval;   /* Consume the current terminal "number" token and ask for a new
       token */
    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());
      }
  
  
    /*
     * Body of the function
     */
  
    /* Get the first token */
    sos_cpu_kstate_switch(& st_parser, st_lexer);
  
    /* Begin the parsing ! */
    *syntax_tree = get_expr();
    /* The result is returned in the syntax_tree parameter */
  
  
 /* Result is the multiplication of 2 big integers */ /*
 big_number_t bn_mult(const big_number_t bn1, const big_number_t bn2)  * Setup the parser's pipeline
   */
  static struct syntax_node * parse_expression(const char *expr)
   int shift = 0;   struct syntax_node *retval = NULL;
   big_number_t retval; 
   int nb_elts; 
   struct digit *d; 
  
   list_init(retval); 
   list_foreach_backward(bn2, d, nb_elts) 
     { 
       big_number_t retmult = bn_muli(bn1, d->value); 
       big_number_t old_retval = retval; 
       bn_shift(& retmult, shift); 
       retval = bn_add(old_retval, retmult); 
       bn_del(& retmult); 
       bn_del(& old_retval); 
       shift ++; 
     } 
    /* 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;   return retval;
 } }
  
  
 /* Result is the factorial of an integer */ /*
 big_number_t bn_fact(unsigned long int v)  * The Evaluator coroutine and associated types/variables
   */
  struct func_eval_params
   unsigned long int i;   const struct syntax_node *e;
   big_number_t retval = bn_new(1);   const char **var_name;
   for (i = 1 ; i <= v ; i++)   int *var_val;
     {   int nb_vars;
       big_number_t I   = bn_new(i); 
       big_number_t tmp = bn_mult(retval, I);   int result;
       sos_x86_videomem_printf(4, 0, };
                               SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, 
                               "%d! = ", (int)i); static void func_eval(struct func_eval_params *parms)
       bn_print_console(4, 8, SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_WHITE, {
                        tmp, 55);   /* The internal (recursive) nested function to evaluate each node of
       bn_del(& I);      the syntax tree */
       bn_del(& retval);   static int rec_eval(const struct syntax_node *n,
       retval = tmp;                       const char* var_name[], int var_val[], int nb_vars)
      {
        switch (n->type)
          {
          case YY_IS_NUM:
            return n->number;
  
          case YY_IS_VAR:
            {
              int i;
              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) ! */
        sos_bochs_printf("ERROR: invalid node type\n");
        sos_cpu_kstate_switch(& st_eval, st_main);
        return -1; /* let's make gcc happy */
  
   return retval; 
    /*
     * 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;
  
  
 void bn_test() /*
   * Function to free the syntax tree
   */
  static void func_free(struct syntax_node *n)
   big_number_t bn = bn_fact(1000);   switch (n->type)
   sos_bochs_printf("1000! = ");     {
   bn_print_bochs(bn);     case YY_IS_NUM:
   sos_bochs_printf("\n");     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);
  
 /* The C entry point of our operating system */   /* 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;
  
   /* 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 364 
Line 1017 
   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);
  
  
   if (magic != MULTIBOOT_BOOTLOADER_MAGIC)   if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
     {     {
Line 388 
Line 1040 
         continue;         continue;
     }     }
  
    /*
     * Some interrupt handlers
     */
  
   /* Binding some HW interrupts and exceptions to software routines */   /* Binding some HW interrupts and exceptions to software routines */
   sos_irq_set_routine(SOS_IRQ_TIMER,   sos_irq_set_routine(SOS_IRQ_TIMER,
                             clk_it);                       clk_it);
   /* Enabling the HW interrupts here, this will make the timer HW 
      interrupt call our clk_it handler */   /*
   asm volatile ("sti\n");    * Setup physical memory management
     */
  
      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 407 
Line 1065 
  
   /* 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,   /* Bind the page fault exception */
                           "Paged-memory mode is activated");   sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT,
                              pgflt_ex);
    /*
     * 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_size))
  
   if (sos_kmalloc_setup())   if (sos_kmalloc_subsystem_setup())
  
   /* Run some kmalloc tests */   /*
   bn_test();    * Enabling the HW interrupts here, this will make the timer HW
     * interrupt call our clk_it handler
     */
    asm volatile ("sti\n");
  
    /*
     * Print hello world using coroutines
     */
    print_hello_world();
  
  
    /*
     * Run coroutine tests
     */
    sos_x86_videomem_printf(4, 0,
                            SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN,
                            "Coroutine test");
    sos_x86_videomem_printf(5, 0,
                            SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW,
                            "Parsing...");
    syntax_tree = parse_expression(" -  ( (69/ toto)+ ( (( - +-- 1))) + --toto*((toto+ - - y - +2*(y-toto))*y) +2*(y-toto) )/- (( y - toto)*2)");
  
    if (syntax_tree != NULL)
      {
        sos_x86_videomem_printf(6, 0,
                                SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW,
                                "Evaluating...");
        sos_x86_videomem_printf(7, 0,
                                SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW,
                                "Result=%d (if 0: check bochs output)",
                                eval_expression(syntax_tree,
                                                (const char*[]){"toto", "y"},
                                                (int[]){3, 4},
                                                2));
        free_syntax_tree(syntax_tree);
        sos_x86_videomem_printf(8, 0,
                                SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW,
                                "Done (un-allocated syntax tree)");
      }
    else
      {
        sos_x86_videomem_printf(6, 0,
                                SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW,
                                "Error in parsing (see bochs output)");
      }
  
    /*
     * Run some demand-paging tests
     */
    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 !
     */
  
   /* An operatig system never ends */   /* An operatig system never ends */
   for (;;)   for (;;)
     continue;     {
        /* Remove this instruction if you get an "Invalid opcode" CPU
           exception (old 80386 CPU) */
        asm("hlt\n");
   return;       continue;
      }
  
 

/tmp/sos-code-article5/sos/physmem.c (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/physmem.c (2005-01-11 09:32:40.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-article5/sos/physmem.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/physmem.h (2005-01-11 09:32:40.000000000 +0100 )
Line 37 
Line 37 
 /** The corresponding mask */ /** The corresponding mask */
 #define SOS_PAGE_MASK  ((1<<12) - 1) #define SOS_PAGE_MASK  ((1<<12) - 1)
  
 #define SOS_PAGE_ALIGN_INF(val) \ #define SOS_PAGE_ALIGN_INF(val)  \
 #define SOS_PAGE_ALIGN_SUP(val) \ #define SOS_PAGE_ALIGN_SUP(val)  \
  #define SOS_IS_PAGE_ALIGNED(val) \
    SOS_IS_ALIGNED((val), SOS_PAGE_SIZE)
 /** /**
  * This is the reserved physical interval for the x86 video memory and  * This is the reserved physical interval for the x86 video memory and
Line 71 
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
Line 98 
Line 99 
  * Increment the reference count of a given physical page. Useful for  * Increment the reference count of a given physical page. Useful for
  * VM code which tries to map a precise physical address.  * VM code which tries to map a precise physical address.
  *  *
   * @param ppage_paddr Physical address of the page (MUST be page-aligned)
   *
  * @return TRUE when the page was previously in use, FALSE when the  * @return TRUE when the page was previously in use, FALSE when the
  * page was previously in the free list, <0 when the page address is  * page was previously in the free list, <0 when the page address is
  * invalid.  * invalid.
Line 110 
Line 113 
  * reference count reaches 0, the page is marked free, ie is available  * reference count reaches 0, the page is marked free, ie is available
  * for future sos_physmem_get_physpage()  * for future sos_physmem_get_physpage()
  *  *
   * @param ppage_paddr Physical address of the page (MUST be page-aligned)
   *
  * @return FALSE when the page is still in use, TRUE when the page is now  * @return FALSE when the page is still in use, TRUE when the page is now
  * unreferenced, <0 when the page address is invalid  * unreferenced, <0 when the page address is invalid
  */  */
Line 121 
Line 126 
 /** /**
  * Return the kernel memory allocation range associated with the given  * Return the kernel memory allocation range associated with the given
  * physical page, or NULL when page has no associated range  * physical page, or NULL when page has no associated range
   *
   * @param ppage_paddr Physical address of the page (MUST be page-aligned)
  */  */
 struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr); struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr);
  
Line 128 
Line 135 
 /** /**
  * Set the kernel memory allocation range associated to the given  * Set the kernel memory allocation range associated to the given
  * physical page.  * physical page.
   *
   * @param ppage_paddr Physical address of the page (MUST be page-aligned)
   *
  * @return error if page is invalid  * @return error if page is invalid
  */  */
 sos_ret_t sos_physmem_set_kmem_range(sos_paddr_t ppage_paddr, sos_ret_t sos_physmem_set_kmem_range(sos_paddr_t ppage_paddr,
  
 

/tmp/sos-code-article5/sos/types.h (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/sos/types.h (2005-01-11 09:32:39.000000000 +0100 )
Line 40 
Line 40 
 typedef unsigned long int  sos_ui32_t; /* 32b unsigned */ typedef unsigned long int  sos_ui32_t; /* 32b unsigned */
 typedef unsigned short int sos_ui16_t; /* 16b unsigned */ typedef unsigned short int sos_ui16_t; /* 16b unsigned */
 typedef unsigned char      sos_ui8_t;  /* 8b unsigned */ typedef unsigned char      sos_ui8_t;  /* 8b unsigned */
  typedef signed long int    sos_si32_t; /* 32b signed */
  typedef signed short int   sos_si16_t; /* 16b signed */
  typedef signed char        sos_si8_t;  /* 8b signed */
  
 typedef enum { FALSE=0, TRUE } sos_bool_t; typedef enum { FALSE=0, TRUE } sos_bool_t;
  
  
 

/tmp/sos-code-article5/support/sos.lds (2004-09-28 09:19:57.000000000 +0200 )
../sos-code-article6/support/sos.lds (2005-01-11 09:32:40.000000000 +0100 )
Line 86 
Line 86 
     .bss SIZEOF(.rodata) + ADDR(.rodata) :     .bss SIZEOF(.rodata) + ADDR(.rodata) :
     {   *(.bss)     {   *(.bss)
         *(COMMON)         *(COMMON)
  
          /* We put the stack of the bootstrap thread on a page
             boundary, because it can be un-allocated later */
          . = ALIGN(4096);
          *(.init_stack)
  
         PROVIDE(ebss = .);         PROVIDE(ebss = .);
         PROVIDE(_ebss = .);         PROVIDE(_ebss = .);
     }     }
  
     /* We take note of the end of the kernel */     /* We take note of the end of the kernel: this is where the GPFM
         will begin */
  
     /* We don't care of the note, indent, comment, etc.. sections     /* We don't care of the note, indent, comment, etc.. sections


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

File list:
    
../sos-code-article6/Makefile
    ../sos-code-article6/VERSION
    ../sos-code-article6/bootstrap/multiboot.S
    ../sos-code-article6/bootstrap/multiboot.h
    ../sos-code-article6/drivers/bochs.c
    ../sos-code-article6/drivers/bochs.h
    ../sos-code-article6/extra/bootsect.S
    ../sos-code-article6/hwcore/cpu_context.c
    ../sos-code-article6/hwcore/cpu_context.h
    ../sos-code-article6/hwcore/cpu_context_switch.S
    ../sos-code-article6/hwcore/exception.c
    ../sos-code-article6/hwcore/exception.h
    ../sos-code-article6/hwcore/exception_wrappers.S
    ../sos-code-article6/hwcore/gdt.c
    ../sos-code-article6/hwcore/gdt.h
    ../sos-code-article6/hwcore/i8259.c
    ../sos-code-article6/hwcore/i8259.h
    ../sos-code-article6/hwcore/idt.c
    ../sos-code-article6/hwcore/idt.h
    ../sos-code-article6/hwcore/irq.c
    ../sos-code-article6/hwcore/irq.h
    ../sos-code-article6/hwcore/irq_wrappers.S
    ../sos-code-article6/hwcore/paging.c
    ../sos-code-article6/hwcore/paging.h
    ../sos-code-article6/hwcore/segment.h
    ../sos-code-article6/sos/assert.c
    ../sos-code-article6/sos/assert.h
    ../sos-code-article6/sos/klibc.c
    ../sos-code-article6/sos/klibc.h
    ../sos-code-article6/sos/kmalloc.c
    ../sos-code-article6/sos/kmalloc.h
    ../sos-code-article6/sos/kmem_slab.c
    ../sos-code-article6/sos/kmem_slab.h
    ../sos-code-article6/sos/kmem_vmm.c
    ../sos-code-article6/sos/kmem_vmm.h
    ../sos-code-article6/sos/macros.h
    ../sos-code-article6/sos/main.c
    ../sos-code-article6/sos/physmem.c
    ../sos-code-article6/sos/physmem.h
    ../sos-code-article6/sos/types.h
    ../sos-code-article6/support/sos.lds