Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

task.c

Go to the documentation of this file.
00001 /*!     \file kernel/task.c
00002  *      \brief Task Management.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update:\n
00005  *              2003-12-15 Andrea Righi:
00006  *                      Added temporary memory operators to get and
00007  *                      free the temporary mapped pages.\n
00008  *              2003-12-16 Andrea Righi:
00009  *                      Fixed mutual exclusion bug in kill() and
00010  *                      waitpid()\n
00011  *      \note Copyright (&copy;) 2003 Andrea Righi
00012  */
00013 
00014 #include <const.h>
00015 #include <stdlib.h>
00016 #include <string.h>
00017 
00018 #include <arch/i386.h>
00019 #include <arch/interrupt.h>
00020 #include <arch/mem.h>
00021 #include <arch/paging.h>
00022 #include <arch/v86.h>
00023 
00024 #include <kernel/console.h>
00025 #include <kernel/clock.h>
00026 #include <kernel/elf32.h>
00027 #include <kernel/fat.h>
00028 #include <kernel/kmalloc.h>
00029 #include <kernel/queue.h>
00030 #include <kernel/semaphore.h>
00031 #include <kernel/shell.h>
00032 #include <kernel/umalloc.h>
00033 
00034 #include <kernel/task.h>
00035 
00036 extern size_t K_PDBR[PAGE_SIZE/sizeof(size_t)];
00037 
00038 /** \ingroup Multitasking
00039  *  \defgroup MTQueue Task Queues
00040  *  Queues for the tasks life-cycle.
00041  *  @{
00042  */
00043 
00044 //! Ready-queue: tasks are ready for the CPU.
00045 queue_t *ready_queue = NULL;
00046 //! Wait-queue: tasks are waiting for I/O.
00047 queue_t *wait_queue = NULL;
00048 //! Zombie-queue: tasks are dying.
00049 queue_t *zombie_queue = NULL;
00050 
00051 /** @} */ // end of MTQueue
00052 
00053 // --- System tasks & threads ------------------------------------------//
00054 
00055 /** \ingroup Multitasking
00056  *  \defgroup MTSystem System Tasks and Threads
00057  *  Here are some systems tasks and threads.
00058  *  @{
00059  */
00060 
00061  //! Current running task.
00062 task_t  *curr_task = NULL;
00063 //! IDLE task structure. Initialized in init_multitasking().
00064 task_t *idle_task = NULL;
00065 //! KPager daemon.
00066 task_t *kpagd = NULL;
00067 
00068 //! \brief Simply do nothing...
00069 /*!
00070  *      This is the default thread to execute when the system has to
00071  *      do nothing.
00072  */
00073 void do_idle()
00074 {
00075         while(TRUE)
00076         {
00077                 enable();
00078                 idle();
00079         }
00080 }
00081 
00082 //! \brief
00083 //!     This special kernel thread provides to free the memory space and
00084 //!     resources owned by the zombie tasks.
00085 void kpager()
00086 {
00087         task_t *t;
00088         dword addr, PDBR;
00089 
00090         while( TRUE )
00091         {
00092                 sched_enter_critical_region();
00093 
00094                 // Look into the zombie queue.
00095                 t = pick_queue(&zombie_queue);
00096                 if ( t==NULL )
00097                 {
00098                         // No task in the zombie queue => IDLE! //
00099                         sched_leave_critical_region();
00100 
00101                         sleep_task( curr_task );
00102                         enable();
00103                         idle();
00104                         continue;
00105                 }
00106 
00107                 // Zombie task found.
00108                 if ( t->type==KTHREAD_T )
00109                 {
00110                         // Free the pl0-stack //
00111                         if( t->pl0_stack!=NULL )
00112                                 kfree((void *)((dword)(t->pl0_stack)+sizeof(uint32_t)-STACK_SIZE));
00113 
00114                         // Destroy the TSS //
00115                         remove_GDT_entry(t->tss_sel);
00116 
00117                         // The task is dead //
00118                         rem_queue(&zombie_queue, t);
00119 
00120                         // Free the task structure //
00121                         kfree((void *)t);
00122                 }
00123                 else
00124                 {
00125                         PDBR = t->tss.cr3;
00126 
00127                         // Temporary switch to the task address space //
00128                         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(t->tss.cr3));
00129 
00130                         // Free the user address space //
00131                         for(addr=0; addr<K_VIR_START; addr+=PAGE_SIZE)
00132                         {
00133                                 if (*ADDR_TO_PDE(addr) != NULL)
00134                                         delete_page(addr);
00135                                 else
00136                                         addr=PAGE_DIR_ALIGN_UP(addr);
00137                         }
00138 
00139                         // Restore the kpager address space //
00140                         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(curr_task->tss.cr3));
00141 
00142                         // Free the pl0-stack //
00143                         kfree((void *)((dword)(t->pl0_stack)+sizeof(uint32_t)-STACK_SIZE));
00144 
00145                         // Destroy the TSS //
00146                         remove_GDT_entry(t->tss_sel);
00147 
00148                         // The task is dead //
00149                         rem_queue(&zombie_queue, t);
00150 
00151                         // Free the task structure //
00152                         kfree((void *)t);
00153 
00154                         // Free the task page directory //
00155                         push_frame(PDBR/PAGE_SIZE);
00156                 }
00157                 sched_leave_critical_region();
00158         }
00159 }
00160 /** @} */ // end of MTSystem
00161 
00162 // --- PID operators ---------------------------------------------------//
00163 
00164 /** \ingroup Multitasking
00165  *  \defgroup MTPid PID Operators
00166  *  Operators to manage the pids for the tasks.
00167  *  @{
00168  */
00169 
00170 //! Last used pid.
00171 atomic_t last_pid;
00172 
00173 //! \brief Create a new pid.
00174 //! \return The new pid created.
00175 /*!
00176  *      This routine generate a pid using an atomic increment.
00177  */
00178 pid_t new_pid()
00179 {
00180         atomic_inc(&last_pid);
00181         return(atomic_read(&last_pid));
00182 }
00183 
00184 //! \brief Return the current task pid if there is a curr_task.
00185 //! \return The current task pid.
00186 pid_t get_pid()
00187 {
00188         if ( !curr_task ) return( NULL );
00189         return( curr_task->pid );
00190 }
00191 /** @} */ // end of MTPid
00192 
00193 // --- Process info ----------------------------------------------------//
00194 
00195 /** \ingroup Multitasking
00196  *  \defgroup MTProcessInfo Process Informations
00197  *  Operators to get some processes informations.
00198  *  @{
00199  */
00200 
00201 //! \brief Get the current task.
00202 //! \return The current task structure pointer.
00203 task_t *get_curr_task()
00204 {
00205         return( curr_task );
00206 }
00207 
00208 //! \brief Return the current task name if there is a curr_task.
00209 //! \return The current task name.
00210 char *get_pname()
00211 {
00212         if ( !curr_task ) return( NULL );
00213         return( curr_task->name );
00214 }
00215 /** @} */ // end of MTProcessInfo
00216 
00217 // --- Task's routines -------------------------------------------------//
00218 
00219 /** \ingroup Multitasking
00220  *  \defgroup MTTaskRoutines Generic Routines
00221  *  The generic routines for task management.
00222  *  @{
00223  */
00224 
00225 //! \brief Create a new kernel thread.
00226 //! \param address The address of the starting point for the thread.
00227 //! \param pname The name of the new thread.
00228 //! \return A pointer to the new-created task structure.
00229 //! \exception NULL If an error occurs (in particular out-of-memory).
00230 /*!
00231  *      This routine initialize a new kernel thread structure and
00232  *      add this thread to the ready queue.
00233  */
00234 task_t *create_kthread(void *address, char *pname)
00235 {
00236         task_t *new_task;
00237         byte *pl0_stack;
00238 
00239         sched_enter_critical_region();
00240 
00241         // --- Create the task structure -------------------------------//
00242         //!
00243         //! IA-32 Intel(R) Architecture Software Developer's Manual
00244         //! Volume 3: System Programming Guide - Chapter 6 - reports:
00245         //!
00246         //! "If paging is used, care should be taken to avoid placing a
00247         //! page boundary within the part of the TSS that the processor
00248         //! reads during a task switch (the first 104 bytes). If a page
00249         //! boundary is placed within this part of the TSS, the pages on
00250         //! either side of the boundary must be present at the same time
00251         //! and contiguous in physical memory.
00252         //!
00253         //! The reason for this restriction is that when accessing a TSS
00254         //! during a task switch, the processor reads and writes into
00255         //! the first 104 bytes of each TSS from contiguous physical
00256         //! addresses beginning with the physical address of the first
00257         //! byte of the TSS. It may not perform address translation at a
00258         //! page boundary if one occurs within this area. So, after the
00259         //! TSS access begins, if a part of the 104 bytes is not both
00260         //! present and physically contiguous, the processor will access
00261         //! incorrect TSS information, without generating a page-fault
00262         //! exception. The reading of this incorrect information will
00263         //! generally lead to an unrecoverable exception later in the
00264         //! task switch process..."
00265         new_task = kmemalign(PAGE_SIZE, sizeof(task_t));
00266         if (new_task == NULL)
00267         {
00268                 // Out of virtual memory!!! //
00269                 set_color(LIGHT_RED);
00270                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00271                 set_color(DEFAULT_COLOR);
00272 
00273                 sched_leave_critical_region();
00274                 return(NULL);
00275         }
00276         memset(new_task, 0, sizeof(task_t));
00277 
00278         // --- Create the pl0-stack --- //
00279         pl0_stack = kmalloc(STACK_SIZE);
00280         if (pl0_stack == NULL)
00281         {
00282                 // Out of virtual memory!!! //
00283                 set_color(LIGHT_RED);
00284                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00285                 set_color(DEFAULT_COLOR);
00286                 // Free the previous allocated space //
00287                 kfree((void *)new_task);
00288 
00289                 sched_leave_critical_region();
00290                 return(NULL);
00291         }
00292         // Null the stack to enforce the page mapping //
00293         memset(pl0_stack, 0, STACK_SIZE);
00294         // Setup the pl0-stack //
00295         new_task->tss.ss0 = new_task->tss.ss = KERNEL_STACK;
00296         new_task->tss.esp0 = new_task->tss.esp = new_task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00297 
00298         // --- Setup the TSS --- //
00299         new_task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(new_task->tss), TSS_SEG, 0);
00300 
00301         // Setup the task PDBR => get the kernel PDBR //
00302         new_task->tss.cr3 = GET_PDBR();
00303 
00304         // Setup the IO port mapping //
00305         new_task->tss.io_map_addr = sizeof(tss_t);
00306 
00307         // Setup general registers //
00308         new_task->tss.ds = new_task->tss.es = KERNEL_DATA;
00309         new_task->tss.fs = new_task->tss.gs = KERNEL_DATA;
00310         new_task->tss.eflags = EFLAGS_IF | 0x02;
00311 
00312         // Initialize general purpose registers //
00313         new_task->tss.eax = new_task->tss.ebx =
00314         new_task->tss.ecx = new_task->tss.edx =
00315         new_task->tss.esi = new_task->tss.edi = 0;
00316 
00317         // Initialize LDTR (Local Descriptor Table Register) //
00318         // No LDTs for now...
00319         new_task->tss.ldtr = 0;
00320 
00321         // Initialize debug trap //
00322         // If set to 1 the processor generate a debug exception when a task switch to this task occurs...
00323         new_task->tss.trace = 0;
00324 
00325         // Setup starting address //
00326         new_task->tss.cs = KERNEL_CODE;
00327         new_task->tss.eip = (dword)address;
00328 
00329         // --- Get a pid --- //
00330         new_task->pid = new_pid();
00331 
00332         // --- Store the name --- //
00333         // The last character must be ever '\0' (end of string) //
00334         strncpy(new_task->name, pname, sizeof(new_task->name)-2);
00335 
00336         // --- Set the type --- //
00337         new_task->type = KTHREAD_T;
00338 
00339         // --- Set the parent process --- //
00340         new_task->father = curr_task;
00341 
00342         // --- Set the console --- //
00343         new_task->console = curr_task->console;
00344 
00345         // --- Insert the task into the ready queue --- //
00346         new_task->state = READY;
00347         add_queue(&ready_queue, new_task);
00348 
00349         sched_leave_critical_region();
00350 
00351         return (new_task);
00352 }
00353 
00354 //! \brief Create a new process.
00355 //! \param address
00356 //!     The address of the starting point for the thread.
00357 //! \param buffer
00358 //!     An address to a buffer where is placed the executable code.
00359 //! \param size
00360 //!     The size of the executable code buffer.
00361 //! \param pname
00362 //!     The name of the new task.
00363 //! \param privilege
00364 //!     The privilege level of the new task:
00365 //!             \li #KERNEL_PRIVILEGE
00366 //!             \li #USER_PRIVILEGE
00367 //! \return A pointer to the new-created task structure.
00368 //! \exception NULL If an error occurs (in particular out-of-memory).
00369 /*!
00370  *      This routine initializes the new task structure, copies
00371  *      the content of the \a buffer to the entry point \a address and
00372  *      then add the new task to the ready queue.
00373  */
00374 task_t *create_process(void *address, void *buffer, size_t size, char *pname, int privilege)
00375 {
00376         task_t *new_task;
00377         byte *pl0_stack;
00378         dword *PDBR;
00379         dword PDBR_frame, i;
00380         register dword cr3;
00381 
00382         sched_enter_critical_region();
00383 
00384         // --- Create the task structure -------------------------------//
00385         //!
00386         //! IA-32 Intel(R) Architecture Software Developer's Manual
00387         //! Volume 3: System Programming Guide - Chapter 6 - reports:
00388         //!
00389         //! "If paging is used, care should be taken to avoid placing a
00390         //! page boundary within the part of the TSS that the processor
00391         //! reads during a task switch (the first 104 bytes). If a page
00392         //! boundary is placed within this part of the TSS, the pages on
00393         //! either side of the boundary must be present at the same time
00394         //! and contiguous in physical memory.
00395         //!
00396         //! The reason for this restriction is that when accessing a TSS
00397         //! during a task switch, the processor reads and writes into
00398         //! the first 104 bytes of each TSS from contiguous physical
00399         //! addresses beginning with the physical address of the first
00400         //! byte of the TSS. It may not perform address translation at a
00401         //! page boundary if one occurs within this area. So, after the
00402         //! TSS access begins, if a part of the 104 bytes is not both
00403         //! present and physically contiguous, the processor will access
00404         //! incorrect TSS information, without generating a page-fault
00405         //! exception. The reading of this incorrect information will
00406         //! generally lead to an unrecoverable exception later in the
00407         //! task switch process..."
00408         new_task = kmemalign(PAGE_SIZE, sizeof(task_t));
00409         if (new_task == NULL)
00410         {
00411                 // Out of virtual memory!!! //
00412                 set_color(LIGHT_RED);
00413                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00414                 set_color(DEFAULT_COLOR);
00415 
00416                 sched_leave_critical_region();
00417                 return(NULL);
00418         }
00419         memset(new_task, 0, sizeof(task_t));
00420 
00421         // --- Create the pl0-stack --- //
00422         pl0_stack = kmalloc(STACK_SIZE);
00423         if (pl0_stack == NULL)
00424         {
00425                 // Out of virtual memory!!! //
00426                 set_color(LIGHT_RED);
00427                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00428                 set_color(DEFAULT_COLOR);
00429                 // Free the previous allocated space //
00430                 kfree((void *)new_task);
00431 
00432                 sched_leave_critical_region();
00433                 return(NULL);
00434         }
00435         // Null the stack to enforce the page mapping //
00436         memset(pl0_stack, 0, STACK_SIZE);
00437         // Setup the pl0-stack //
00438         new_task->tss.ss0 = KERNEL_STACK;
00439         new_task->tss.esp0 = new_task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00440 
00441         // --- Setup the TSS --- //
00442         new_task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(new_task->tss), TSS_SEG, 0);
00443 
00444         // --- Create the virtual space of the task ------------------- //
00445         PDBR = (dword *)get_temp_page();
00446         if ( PDBR == NULL )
00447         {
00448                 // Out of memory!!! Free the previous allocated memory. //
00449                 set_color(LIGHT_RED);
00450                 kprintf("\n\rOut memory!!! Cannot create task [%s]\n\r", pname);
00451                 set_color(DEFAULT_COLOR);
00452                 // Free the previous allocated space //
00453                 kfree((void *)(pl0_stack));
00454                 kfree((void *)new_task);
00455                 // Destroy the TSS //
00456                 remove_GDT_entry(new_task->tss_sel);
00457                 // Free the
00458 
00459                 sched_leave_critical_region();
00460                 return(NULL);
00461         }
00462         PDBR_frame = vir_to_phys( (dword)PDBR );
00463 
00464         // Initialize PDBR //
00465         memset(PDBR, 0, PAGE_SIZE);
00466         for (i=K_VIR_START/(PAGE_SIZE*1024); i<1024; i++)
00467                 PDBR[i] = K_PDBR[i];
00468 
00469         // Map page directory into itself //
00470         PDBR[1023] = PDBR_frame | P_PRESENT | P_WRITE;
00471 
00472         // Unmap the temporary page, without free the physical frame    //
00473         free_temp_page(PDBR, FALSE);
00474 
00475         // Temporary switch to the new address space                    //
00476         //                                                              //
00477         // QUESTION: cr3 is in the stack... if I switch into the new    //
00478         // address space can I ever see the old cr3 value???            //
00479         //                                                              //
00480         // RE: Yes, but only if cr3 is stored into a register...        //
00481         // And if the value should be stored into the stack, here we    //
00482         // are at CPL0, so we're using the 0-privileged stack, so,      //
00483         // since the 0-privileged stack space is shared between every   //
00484         // task and also the kernel, we're ever able to see the stored  //
00485         // cr3 value...                                                 //
00486         __asm__ __volatile__ ("movl %%cr3, %0" : "=r"(cr3) : );
00487         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(PDBR_frame));
00488 
00489         // --- Create the user space --- //
00490 
00491         // Create the task stack //
00492         new_task->tss.ss = (privilege == KERNEL_PRIVILEGE) ? KERNEL_STACK : USER_STACK | 3;
00493         if ( privilege == KERNEL_PRIVILEGE )
00494                 new_task->tss.esp = new_task->pl0_stack;
00495         else
00496                 new_task->tss.esp = (dword)(TASK_STACK_START-sizeof(size_t));
00497 
00498         // Map the user code and data space.                            //
00499         // ("address" is the virtual task space, "buffer" is the        //
00500         // kernel space where the task-code is located).                //
00501         if (address != buffer)
00502         {
00503                 memcpy(address, buffer, size);
00504         }
00505 
00506         // Restore the old address space //
00507         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(cr3));
00508 
00509         // Setup the task PDBR //
00510         new_task->tss.cr3 = PDBR_frame;
00511 
00512         // Setup the IO port mapping //
00513         new_task->tss.io_map_addr = sizeof(tss_t);
00514 
00515         // Setup general registers //
00516         if ( privilege == KERNEL_PRIVILEGE )
00517                 new_task->tss.ds = new_task->tss.es =
00518                 new_task->tss.fs = new_task->tss.gs = KERNEL_DATA;
00519         else
00520                 new_task->tss.ds = new_task->tss.es =
00521                 new_task->tss.fs = new_task->tss.gs = USER_DATA | 3;
00522 
00523         new_task->tss.eflags = EFLAGS_IF | 0x02;
00524         if ( privilege == USER_PRIVILEGE )
00525                 // User tasks must have the I/O privilege level         //
00526                 // at the lower level                                   //
00527                 new_task->tss.eflags |= EFLAGS_IOPL3;
00528 
00529         // Initialize general purpose registers //
00530         new_task->tss.eax = new_task->tss.ebx =
00531         new_task->tss.ecx = new_task->tss.edx =
00532         new_task->tss.esi = new_task->tss.edi = 0;
00533 
00534         // Initialize LDTR (Local Descriptor Table Register) //
00535         // No LDTs for now...
00536         new_task->tss.ldtr = 0;
00537 
00538         // Initialize debug trap //
00539         // If set to 1 the processor generates a debug exception when a //
00540         // task switch to this task occurs...                           //
00541         new_task->tss.trace = 0;
00542 
00543         // Setup starting address //
00544         new_task->tss.cs = (privilege == KERNEL_PRIVILEGE) ? KERNEL_CODE : USER_CODE | 3;
00545         new_task->tss.eip = (dword)address;
00546 
00547         // --- Get a pid --- //
00548         new_task->pid = new_pid();
00549 
00550         // --- Store the name --- //
00551         // The last character must be ever '\0' (end of string) //
00552         strncpy(new_task->name, pname, sizeof(new_task->name)-2);
00553 
00554         // --- Set the type --- //
00555         new_task->type = PROCESS_T;
00556 
00557         // --- Set the parent process --- //
00558         new_task->father = curr_task;
00559 
00560         // --- Set the console --- //
00561         if( curr_task )
00562                 new_task->console = curr_task->console;
00563 
00564         // --- Insert the task into the ready queue --- //
00565         new_task->state = READY;
00566         add_queue(&ready_queue, new_task);
00567 
00568         sched_leave_critical_region();
00569         return (new_task);
00570 }
00571 
00572 // --- Task execution module ------------------------------------------ //
00573 
00574 //! \brief Create a new virtual space.
00575 //! \return
00576 //!     The physical address of the page directory for the new
00577 //!     virtual space.
00578 //! \exception NULL If an error occurs (in particular out-of-memory).
00579 size_t new_vspace()
00580 {
00581         size_t PDBR_frame;
00582         uint32_t *PDBR;
00583         int i;
00584 
00585         // Create the virtual space of the task                         //
00586         PDBR = (uint32_t *)get_temp_page();
00587         if (PDBR == NULL)
00588         {
00589                 // Out of memory!!!                                     //
00590                 return( NULL );
00591         }
00592         PDBR_frame = vir_to_phys( (dword)PDBR );
00593 
00594         // Initialize PDBR                                              //
00595         memset(PDBR, 0, PAGE_SIZE);
00596         for (i=K_VIR_START/(PAGE_SIZE*1024); i<1024; i++)
00597                 PDBR[i] = K_PDBR[i];
00598         // Map page directory into itself                               //
00599         PDBR[1023] = PDBR_frame | P_PRESENT | P_WRITE;
00600 
00601         // Free the page, but doesn't destroy the physical frame        //
00602         free_temp_page( PDBR, FALSE );
00603 
00604         return( PDBR_frame );
00605 }
00606 
00607 //! \brief Initialize the stack for a new process.
00608 //! \param argc
00609 //!     The number of arguments passed to the new process.
00610 //! \param argv
00611 //!     A pointer to the argument strings.
00612 //!     <b>NOTE: The content of \p argv may be changed!!!</b>
00613 //! \param stack_start
00614 //!     Where the stack is placed in memory.
00615 //! \param stack_size
00616 //!     The size of the stack in bytes.
00617 //! \param privilege
00618 //!     The privilege of the task.
00619 //! \return The starting stack pointer after initialization.
00620 uint32_t *task_setup_stack(int argc, char **argv, addr_t stack_start, size_t stack_size, int privilege)
00621 {
00622         static uint8_t __i386_quit[] = {
00623                 0xb8, 0x00, 0x00, 0x00, 0x00,   // mov $0x0, %eax
00624                 0x89, 0xe5,                     // mov %esp, %ebp
00625                 0xcd, 0x80,                     // int $0x80
00626         };
00627         size_t *stack = (uint32_t *)( (stack_start - sizeof(size_t)) -
00628                                         ALIGN_UP(sizeof(size_t), sizeof(__i386_quit)) -
00629                                         sizeof(size_t) );
00630         size_t *exit_point = stack;
00631         size_t *user_argv;
00632         int i;
00633 
00634         // We have not to null the stack to enforce page mapping for a  //
00635         // user-mode task, because a stack fault is managed like a      //
00636         // normal page fault.                                           //
00637         // But In kernel mode a stack fault can cause a system reboot   //
00638         // so we must enforce the page mapping in this case.            //
00639         if ( privilege == KERNEL_PRIVILEGE )
00640                 memset((void *)(stack_start-stack_size), 0, stack_size/sizeof(size_t));
00641 
00642         // Copy the exit code into the stack, because if we are in      //
00643         // user-mode we cannot access to the kernel space without       //
00644         // causing a page level protection fault.                       //
00645         memcpy((void *)stack, __i386_quit, sizeof(__i386_quit));
00646 
00647         // Copy external parameters strings                             //
00648         for( i=0; i<argc; i++ )
00649         {
00650                 stack = (size_t *)((size_t)stack - strlen(argv[i]) - 1);
00651                 strcpy((char *)stack, argv[i]);
00652                 argv[i] = (char *)stack;
00653         }
00654 
00655         // Round down the stack pointer to the stack boundaries         //
00656         stack = (size_t *)( TRUNC((size_t)stack, sizeof(size_t)) );
00657 
00658         // Copy parameter string pointers                               //
00659         for( i=0; i<argc; i++ )
00660         {
00661                 *(--stack) = (size_t)(argv[argc-i-1]);
00662         }
00663 
00664         // Set the process argv pointer                                 //
00665         user_argv = stack;
00666         *(--stack) = (size_t)user_argv;
00667         // Copy number of parameters value                              //
00668         *(--stack) = argc;
00669         // Copy the exit point address                                  //
00670         *(--stack) = (size_t)(exit_point);
00671 
00672         // Return the initial stack pointer                             //
00673         return( stack );
00674 }
00675 
00676 //! \brief Execute a file creating a new task.
00677 //! \param filename
00678 //!     The name of the file to execute.
00679 //! \param argc
00680 //!     The number of arguments passed to the new process.
00681 //! \param argv
00682 //!     The arguments passed to the new process.
00683 //! \param argv
00684 //!     A pointer to the argument strings.
00685 //! \param privilege
00686 //!     The privilege level of the new task:
00687 //!             \li #KERNEL_PRIVILEGE
00688 //!             \li #USER_PRIVILEGE
00689 //! \return A pointer to the new-created task structure.
00690 //! \exception NULL If an error occurs (in particular out-of-memory).
00691 /*!
00692  *      This routine can execute only ELF32 files. The file is loaded
00693  *      in a temporary buffer, then the sections are copied into memory
00694  *      and the task structure is initialized. At the end of this the
00695  *      new task is putted into the ready queue.
00696  */
00697 task_t *new_task(char *filename, int argc, char **argv, int privilege)
00698 {
00699         uint32_t temp_PDBR;
00700         task_t  *task;
00701         byte    *pl0_stack;
00702         size_t  PDBR, entry;
00703         void    *filebuffer;
00704         int     filesize = get_file_size(filename);
00705 
00706         if ( filesize < 0 )
00707                 return(NULL);
00708 
00709         // Load the ELF32 file into the buffer //
00710         filebuffer = kmalloc(filesize);
00711         if ( elf32_load_file(filename, filebuffer)<0 )
00712         {
00713                 kfree(filebuffer);
00714                 return(NULL);
00715         }
00716 
00717         sched_enter_critical_region();
00718 
00719         // Create the task structure //
00720         task = kmemalign(PAGE_SIZE, sizeof(task_t));
00721         if ( task == NULL )
00722         {
00723                 sched_leave_critical_region();
00724                 kfree(filebuffer);
00725                 return(NULL);
00726         }
00727         memset(task, 0, sizeof(task_t));
00728 
00729         // Create the PL0-stack //
00730         pl0_stack = kmalloc(STACK_SIZE);
00731         if (pl0_stack == NULL)
00732         {
00733                 // Out of virtual memory!!! //
00734                 // Free the previous allocated space //
00735                 kfree((void *)task);
00736                 kfree(filebuffer);
00737 
00738                 sched_leave_critical_region();
00739                 return(NULL);
00740         }
00741         // Null the stack to enforce the page mapping //
00742         memset(pl0_stack, 0, STACK_SIZE);
00743 
00744         // Setup the PL0-stack //
00745         task->tss.ss0 = KERNEL_STACK;
00746         task->tss.esp0 = task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00747 
00748         // Setup the TSS //
00749         task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(task->tss), TSS_SEG, 0);
00750 
00751         // Create the new virtual space //
00752         PDBR = new_vspace();
00753         if ( PDBR == NULL )
00754         {
00755                 // Out of memory !!! //
00756 
00757                 // Destroy the TSS //
00758                 remove_GDT_entry(task->tss_sel);
00759                 // Free the previous allocated space //
00760                 kfree((void *)(pl0_stack));
00761                 kfree((void *)task);
00762                 kfree(filebuffer);
00763 
00764                 sched_leave_critical_region();
00765                 return(NULL);
00766         }
00767 
00768         // Temporary switch to the new address space //
00769         __asm__ __volatile__ ("movl %%cr3, %0" : "=r"(temp_PDBR) : );
00770         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(PDBR));
00771 
00772         // Copy the file sections into memory //
00773         entry = elf32_copy_sections(filebuffer);
00774         if ( entry == NULL )
00775         {
00776                 // Restore the old address space //
00777                 __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(temp_PDBR));
00778 
00779                 // Free the PDBR frame //
00780                 push_frame(PDBR/PAGE_SIZE);
00781                 // Destroy the TSS //
00782                 remove_GDT_entry(task->tss_sel);
00783                 // Free the previous allocated space //
00784                 kfree((void *)(pl0_stack));
00785                 kfree((void *)task);
00786                 kfree(filebuffer);
00787 
00788                 sched_leave_critical_region();
00789                 return(NULL);
00790         }
00791 
00792         // Create the task stack.
00793         task->tss.ss = (privilege == KERNEL_PRIVILEGE) ? KERNEL_STACK : USER_STACK | 3;
00794         if ( privilege == KERNEL_PRIVILEGE )
00795                 task->tss.esp = (uint32_t)task_setup_stack(argc, argv, task->pl0_stack, STACK_SIZE, privilege);
00796         else
00797                 task->tss.esp = (uint32_t)task_setup_stack(argc, argv, TASK_STACK_START, STACK_SIZE, privilege);
00798 
00799         // Initialize the user heap.
00800         umalloc_init( TASK_HEAP_START, TASK_HEAP_DEFAULT_SIZE );
00801 
00802         // Restore the old address space //
00803         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(temp_PDBR));
00804 
00805         // Setup the task PDBR //
00806         task->tss.cr3 = PDBR;
00807 
00808         // Setup the IO port mapping //
00809         task->tss.io_map_addr = sizeof(tss_t);
00810 
00811         // Setup general registers //
00812         if ( privilege == KERNEL_PRIVILEGE )
00813                 task->tss.ds = task->tss.es =
00814                 task->tss.fs = task->tss.gs = KERNEL_DATA;
00815         else
00816                 task->tss.ds = task->tss.es =
00817                 task->tss.fs = task->tss.gs = USER_DATA | 3;
00818 
00819         task->tss.eflags = EFLAGS_IF | 0x02;
00820         if ( privilege == USER_PRIVILEGE )
00821                 // User privilege must be the I/O privilege set to the  //
00822                 // lower level                                          //
00823                 task->tss.eflags |= EFLAGS_IOPL3;
00824 
00825         // Initialize general purpose registers //
00826         task->tss.eax = task->tss.ebx =
00827         task->tss.ecx = task->tss.edx =
00828         task->tss.esi = task->tss.edi = 0;
00829 
00830         // Initialize LDTR (Local Descriptor Table Register) //
00831         // No LDTs for now...
00832         task->tss.ldtr = 0;
00833 
00834         // Initialize debug trap //
00835         // If set to 1 the processor generate a debug exception when a task switch to this task occurs...
00836         task->tss.trace = 0;
00837 
00838         // Setup starting address //
00839         task->tss.cs = (privilege == KERNEL_PRIVILEGE) ? KERNEL_CODE : USER_CODE | 3;
00840         task->tss.eip = entry;
00841 
00842         // Get a pid //
00843         task->pid = new_pid();
00844 
00845         // Store the name //
00846         // The last character must be ever '\0' (end of string) //
00847         strncpy(task->name, filename, sizeof(task->name)-2);
00848 
00849         // Set the type //
00850         task->type = PROCESS_T;
00851 
00852         // Set the parent process //
00853         task->father = curr_task;
00854 
00855         // Set the console //
00856         task->console = curr_task->console;
00857 
00858         // Insert the task into the ready queue //
00859         task->state = READY;
00860         add_queue(&ready_queue, task);
00861 
00862         // Well done! //
00863         kfree(filebuffer);
00864         sched_leave_critical_region();
00865 
00866         return( task );
00867 }
00868 
00869 // --- END of Task execution module ----------------------------------- //
00870 
00871 //! \brief Kill a task by the pid.
00872 //! \param pid The process id.
00873 //! \return
00874 //!     \li #TRUE if the task has been successfully killed
00875 //!     \li #FALSE if the task cannot be killed.
00876 /*!
00877  *      This routine move the task having the \a pid from the ready queue
00878  *      to the zombie queue. Then the kpager() daemon should provide
00879  *      to free the memory and resources owned by this task.
00880  */
00881 bool kill(pid_t pid)
00882 {
00883         // Queues where to search the processes                         //
00884         static queue_t **q[] = { &ready_queue, &wait_queue };
00885         task_t *p = curr_task;
00886         bool retval = FALSE;
00887         register int i, j, n;
00888 
00889         // No running processes(???) => quit!                           //
00890         if ( p==NULL )
00891                 return(FALSE);
00892 
00893         sched_enter_critical_region();
00894 
00895         // Explore every task queue                                     //
00896         for ( i=0; i<_countof(q); i++)
00897                 for ( j=0, n=count_queue(q[i]); j<n; j++ )
00898                 {
00899                         // Get the next task in the queue               //
00900                         p = pick_queue( q[i] );
00901 
00902                         if ( p->pid == pid )
00903                         {
00904                                 // Move the task from the queue to the  //
00905                                 // zombie queue                         //
00906                                 if ( rem_queue(q[i], p) )
00907                                 {
00908                                         add_queue(&zombie_queue, p);
00909                                         p->state = ZOMBIE;
00910                                         // Wake-up the kpager daemon    //
00911                                         wakeup_task( kpagd );
00912                                         // Wake-up the father           //
00913                                         if ( p->father != NULL )
00914                                                 wakeup_task( p->father );
00915                                         // Task killed!                 //
00916                                         retval = TRUE;
00917                                 }
00918                         }
00919                         else
00920                         {
00921                                 // If the process p is a child of the   //
00922                                 // process to kill we have to set its   //
00923                                 // father to NULL                       //
00924                                 if ( p->father != NULL )
00925                                         if ( (p->father)->pid == pid )
00926                                                 p->father = NULL;
00927                         }
00928                 }
00929 
00930         // Check if the task has killed itself                          //
00931         if (curr_task->pid == pid)
00932         {
00933                 sched_leave_critical_region();
00934                 // Wait for the dispatcher                              //
00935                 do_idle();
00936         }
00937 
00938         sched_leave_critical_region();
00939         return( retval );
00940 }
00941 
00942 //! \brief Kill the current running task.
00943 //! \param n The return code.
00944 /*!
00945  *      This routine move the current task from the ready queue
00946  *      to the zombie queue. Then the kpager() daemon should provide
00947  *      to free the memory and resources owned by this task.
00948  */
00949 void auto_kill(int n)
00950 {
00951         kill( curr_task->pid );
00952 }
00953 
00954 //! \brief Wait for the exit of a child.
00955 //! \param pid
00956 //!     \li The child process id
00957 //!     \li -1 to wait for the exit of a generic child.
00958 //! \param status Reports the status of the child.
00959 //! \param options Options flags.
00960 //! \return
00961 //!     The pid of the child or -1 if the process has not been
00962 //!     found.
00963 /*!
00964  *      \todo \a status flags and \a options flags not yet implemented!
00965  */
00966 pid_t waitpid(pid_t pid, int *status, int options)
00967 {
00968         // Queues where to search the processes                         //
00969         static queue_t **q[] = { &ready_queue, &wait_queue };
00970         task_t *p;
00971         int i, j, n;
00972         bool found = FALSE;
00973         dword IF = GET_IF();
00974 
00975         // A task can't wait for yourself!                              //
00976         if (curr_task->pid == pid) return(0);
00977 
00978 repeat:
00979         sched_enter_critical_region();
00980         // Search into every task queue                                 //
00981         for( i=0; i<_countof(q); i++ )
00982         {
00983                 for( j=0, n = count_queue( q[i] ); j<n; j++ )
00984                 {
00985                         p = pick_queue( q[i] );
00986                         if ( (p->father == curr_task) && ((pid==-1) || (p->pid==pid)) )
00987                         {
00988                                 // Found!                               //
00989                                 found = TRUE;
00990 
00991                                 // Update the pid of the child that we  //
00992                                 // are waiting (when pid is -1).        //
00993                                 pid = p->pid;
00994 
00995                                 // Move the current task (father) into  //
00996                                 // the wait queue and wait              //
00997                                 if ( rem_queue(&ready_queue, curr_task) )
00998                                 {
00999                                         add_queue(&wait_queue, curr_task);
01000                                         curr_task->state = WAIT;
01001                                 }
01002                                 sched_leave_critical_region();
01003 
01004                                 // Sleeped!                             //
01005                                 // Now wait for the dispatcher          //
01006                                 enable();
01007                                 idle();
01008                                 // Task waked-up! Check if the child    //
01009                                 // that we are waiting is dead          //
01010                                 goto repeat;
01011                         }
01012                 }
01013 
01014         }
01015         sched_leave_critical_region();
01016         SET_IF(IF);
01017 
01018         // Return the child pid or -1 if the process has not been found //
01019         return( (found) ? pid : -1 );
01020 }
01021 
01022 //! \brief Sleep a process if it is awake.
01023 //! \param p The structure of the task to sleep.
01024 void sleep_task(task_t *p)
01025 {
01026         p->state = WAIT;
01027 }
01028 
01029 //! \brief Wakeup a process if it is sleeping.
01030 //! \param p The structure of the task to wakeup.
01031 void wakeup_task(task_t *p)
01032 {
01033         p->state = READY;
01034 }
01035 /** @} */ // end of MTTaskRoutines
01036 
01037 // --- Scheduling routines ---------------------------------------------//
01038 
01039 /** \ingroup Multitasking
01040  *  \defgroup MTScheduling The Scheduler
01041  *  The routines for the tasks scheduling.
01042  *  @{
01043  */
01044 
01045 //! A semaphore to enable or disable scheduling (used to manage
01046 //! critical regions).
01047 semaphore_t sched_enabled;
01048 
01049 //! \brief Disable the scheduling of the other tasks.
01050 //! \warning
01051 //!     \b ATTENTION!!! Remember to call sched_leave_critical_region()
01052 //!     when the task exits from the critical region. Otherwise the
01053 //!     whole system could be blocked, because the scheduling of the
01054 //!     other tasks is disabled after a task calls this function.
01055 void sched_enter_critical_region()
01056 {
01057         DOWN( &sched_enabled );
01058 }
01059 
01060 //! \brief Re-enable scheduling for the other tasks.
01061 void sched_leave_critical_region()
01062 {
01063         UP( &sched_enabled );
01064 }
01065 
01066 //! \brief Return if the scheduling is enabled or not.
01067 //! \return
01068 //!     \li #TRUE if the scheduling is enabled
01069 //!     \li #FALSE otherwise.
01070 bool is_sched_enabled()
01071 {
01072         if( atomic_read(&sched_enabled) )
01073                 return( TRUE );
01074         else
01075                 return( FALSE );
01076 }
01077 
01078 //! \brief This is a simple round robin scheduler.
01079 /*!
01080  *      This routine is called by the dispatcher() every time
01081  *      a task must be scheduled.
01082  */
01083 static __inline__ void scheduler()
01084 {
01085         task_t *p=NULL;
01086         int count;
01087 
01088         // Look for the tasks that need to sleep.
01089         count = count_queue( &ready_queue );
01090         for ( ; count; --count )
01091         {
01092                 p = pick_queue( &ready_queue );
01093                 if ( p->state==WAIT )
01094                 {
01095                         rem_queue( &ready_queue, p );
01096                         add_queue( &wait_queue,  p );
01097                 }
01098         }
01099 
01100         // Look for the tasks that need to wake-up.
01101         count = count_queue( &wait_queue );
01102         for ( ; count; --count )
01103         {
01104                 p = pick_queue( &wait_queue );
01105                 if ( p->state==READY )
01106                 {
01107                         rem_queue( &wait_queue,  p );
01108                         add_queue( &ready_queue, p );
01109                 }
01110         }
01111 
01112         // Select a new task to run (Round Robin).
01113         curr_task = pick_queue( &ready_queue );
01114         if (curr_task == NULL) curr_task=idle_task;
01115 }
01116 /** @} */ // end of MTScheduling
01117 
01118 /** \ingroup Multitasking
01119  *  \defgroup MTDispatcher The Dispatcher
01120  *  The dispatcher routine.
01121  *  @{
01122  */
01123 
01124 //! \brief The dispatcher.
01125 /*!
01126  *      This routine supplies to change the context of the tasks.
01127  *      It is invoked at every clock tick.\n
01128  *      You can change the frequency of the ticks changing the value
01129  *      of the #HZ definition.
01130  */
01131 void dispatcher()
01132 {
01133         task_t *prev_task;
01134 
01135         // Call the clock task at every tick                            //
01136         clock_thread();
01137 
01138         // Can we switch to another process?!                           //
01139         if ( atomic_read(&sched_enabled) )
01140         {
01141                 // Store the previous task                              //
01142                 prev_task = curr_task;
01143                 // Select a new task                                    //
01144                 scheduler();
01145 
01146                 if ( prev_task != curr_task )
01147                 {
01148                         if ( prev_task->tss.cr3 != curr_task->tss.cr3 )
01149                         {
01150                                 // Update the page directory of the     //
01151                                 // new task with the kernel page        //
01152                                 // directory in the kernel memory area. //
01153                                 __asm__ __volatile__ (
01154                                         "movl %%eax, %%cr3\n"
01155                                         "cld\n"
01156                                         "rep movsl\n"
01157                                         "movl %%ebx, %%cr3" : :
01158                                         "a"(curr_task->tss.cr3),
01159                                         "b"(prev_task->tss.cr3),
01160                                         "D"((dword *)PAGE_DIR_MAP+(K_VIR_START/(PAGE_SIZE*1024))),
01161                                         "S"((dword *)K_PDBR+(K_VIR_START/(PAGE_SIZE*1024))),
01162                                         "c"((PAGE_DIR_MAP/(PAGE_SIZE*1024)) - (K_VIR_START/(PAGE_SIZE*1024)))
01163                                 );
01164                                 __asm__("" : : : "%eax", "%ebx", "%ecx", "%edi", "%esi");
01165                         }
01166 
01167                         // Re-enable master interrupt controller        //
01168                         outportb(PORT_8259_M, EOI);
01169                         // Perform the task switch                      //
01170                         jmp_to_tss(curr_task->tss_sel);
01171                 }
01172         }
01173 }
01174 /** @} */ // end of MTDispatcher
01175 
01176 // --- Initialization --------------------------------------------------//
01177 
01178 /** \ingroup Multitasking
01179  *  \defgroup MTInit Task Initialization
01180  *  The multitasking initialization routine.
01181  *  @{
01182  */
01183 
01184 //! \brief Initialize the multitasking management.
01185 void init_multitasking()
01186 {
01187         // Initialize pid counter.
01188         atomic_set(&last_pid, 0);
01189 
01190         // Initialize scheduler critical region flag.
01191         INIT_MUTEX( &sched_enabled );
01192 
01193         // Create the "init" task.
01194         curr_task = create_process(NULL, NULL, 0, "init", KERNEL_PRIVILEGE);
01195         // Set the console.
01196         curr_task->console=1;
01197         // Set the type.
01198         curr_task->type = KTHREAD_T;
01199 
01200         // Load task register.
01201         __asm__ __volatile__ ("ltr %%ax" : : "a" (curr_task->tss_sel));
01202         __asm__ __volatile__ ("" : : : "ax");
01203 
01204         // Load virtual space.
01205         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(curr_task->tss.cr3));
01206 
01207         // Initialize the IDLE task.
01208         // The IDLE task is a special task,
01209         // it's always ready but never present in the ready queue.
01210         idle_task = create_kthread(&do_idle, "idle");
01211         idle_task->console=0;
01212         rem_queue(&ready_queue, idle_task);
01213 
01214         // Initialize KPager thread
01215         kpagd = create_kthread(&kpager, "kpagerd");
01216         kpagd->console = 0;
01217 
01218         // Install the dispatcher.
01219         install_irq_handler( TIMER_IRQ, (void *)&dispatcher );
01220 }
01221 /** @} */ // end of MTInit
01222 
01223 // --- Debug procedures ------------------------------------------------//
01224 
01225 /** \ingroup Multitasking
01226  *  \defgroup MTDebug Debug
01227  *  Debug procedures for multitasking.
01228  *  @{
01229  */
01230 
01231 //! \brief
01232 //!     Print the state of every process from the ready, wait
01233 //!     and zombie queues.
01234 void ps()
01235 {
01236         task_t *p;
01237         int count, i;
01238 
01239         if ((p = curr_task)==NULL) return;
01240 
01241         // Header //
01242         kprintf("\n\rPID       STATE       CONSOLE     CMD");
01243 
01244         sched_enter_critical_region();
01245 
01246         // Ready processes //
01247         for(i=0, count=count_queue(&ready_queue); i<count; i++)
01248         {
01249                 // Get the next task in the ready queue //
01250                 p = pick_queue(&ready_queue);
01251 
01252                 // Print process informations //
01253                 kprintf("\n\r%u", p->pid);
01254                 gotoxy(12, -1);
01255                 kputchar('R');
01256                 gotoxy(22, -1);
01257                 if (p->console)
01258                         kprintf("tty%u", p->console);
01259                 else
01260                         kputchar('?');
01261                 gotoxy(34, -1);
01262                 kprintf("%s", p->name);
01263         }
01264 
01265         // Wait processes //
01266         for(i=0, count=count_queue(&wait_queue); i<count; i++)
01267         {
01268                 // Get the next task in the wait queue //
01269                 p = pick_queue(&wait_queue);
01270 
01271                 // Print process informations //
01272                 kprintf("\n\r%u", p->pid);
01273                 gotoxy(12, -1);
01274                 kputchar('W');
01275                 gotoxy(22, -1);
01276                 if (p->console)
01277                         kprintf("tty%u", p->console);
01278                 else
01279                         kputchar('?');
01280                 gotoxy(34, -1);
01281                 kprintf("%s", p->name);
01282         }
01283 
01284         // Zombie processes //
01285         for(i=0, count=count_queue(&zombie_queue); i<count; i++)
01286         {
01287                 // Get the next task in the zombie queue //
01288                 p = pick_queue(&zombie_queue);
01289 
01290                 // Print process informations //
01291                 kprintf("\n\r%u", p->pid);
01292                 gotoxy(12, -1);
01293                 kputchar('Z');
01294                 gotoxy(22, -1);
01295                 if (p->console)
01296                         kprintf("tty%u", p->console);
01297                 else
01298                         kputchar('?');
01299                 gotoxy(34, -1);
01300                 kprintf("%s", p->name);
01301         }
01302 
01303         sched_leave_critical_region();
01304 
01305         kprintf("\n\r");
01306 }
01307 /** @} */ // end of MTDebug

Generated on Fri Feb 20 15:32:16 2004 for Minirighi by doxygen1.2.18