exec.c

Go to the documentation of this file.
00001 /*****************************************************************************************
00002  Copyright (c) 2002-2004 The UbixOS Project
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without modification, are
00006  permitted provided that the following conditions are met:
00007 
00008  Redistributions of source code must retain the above copyright notice, this list of
00009  conditions, the following disclaimer and the list of authors.  Redistributions in binary
00010  form must reproduce the above copyright notice, this list of conditions, the following
00011  disclaimer and the list of authors in the documentation and/or other materials provided
00012  with the distribution. Neither the name of the UbixOS Project nor the names of its
00013  contributors may be used to endorse or promote products derived from this software
00014  without specific prior written permission.
00015 
00016  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
00017  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00018  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
00019  THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00020  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00021  OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00022  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
00023  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00024  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00025 
00026  $Id$
00027 
00028 *****************************************************************************************/
00029 
00030 #include <ubixos/exec.h>
00031 #include <ubixos/sched.h>
00032 #include <ubixos/ld.h>
00033 #include <ubixos/kpanic.h>
00034 #include <ubixos/endtask.h>
00035 #include <vmm/vmm.h>
00036 #include <lib/kmalloc.h>
00037 #include <lib/kprintf.h>
00038 #include <lib/string.h>
00039 #include <assert.h>
00040 
00041 #define STACK_ADDR 0xC800000
00042 
00043 /*****************************************************************************************
00044 
00045  Function:   execThread(void (*)(void),int,char *);
00046  Description: This function will create a thread from code in the current memory space
00047 
00048  Notes:
00049  
00050  05/19/04 - This does not work the way I want it to it still makes a copy of kernel space
00051             so do not use out side of kernel space
00052 
00053 *****************************************************************************************/
00054 uInt32 execThread(void (* tproc)(void),uInt32 stack,char *arg) {
00055   kTask_t * newProcess = 0x0;
00056   /* Find A New Thread */
00057   newProcess = schedNewTask();
00058   assert(newProcess); 
00059   if (stack < 0x100000)
00060     kpanic("exec: stack not in valid area: [0x%X]\n",stack);
00061 
00062   /* Set All The Correct Thread Attributes */
00063   newProcess->tss.back_link    = 0x0;
00064   newProcess->tss.esp0         = 0x0;
00065   newProcess->tss.ss0          = 0x0;
00066   newProcess->tss.esp1         = 0x0;
00067   newProcess->tss.ss1          = 0x0;
00068   newProcess->tss.esp2         = 0x0;
00069   newProcess->tss.ss2          = 0x0;
00070   newProcess->tss.cr3          = (unsigned int)kernelPageDirectory;
00071   newProcess->tss.eip          = (unsigned int)tproc;
00072   newProcess->tss.eflags       = 0x206;
00073   newProcess->tss.esp          = stack;
00074   newProcess->tss.ebp          = stack;
00075   newProcess->tss.esi          = 0x0;
00076   newProcess->tss.edi          = 0x0;
00077 
00078   /* Set these up to be ring 3 tasks */
00079   /*
00080   newProcess->tss.es           = 0x30+3;
00081   newProcess->tss.cs           = 0x28+3;
00082   newProcess->tss.ss           = 0x30+3;
00083   newProcess->tss.ds           = 0x30+3;
00084   newProcess->tss.fs           = 0x30+3;
00085   newProcess->tss.gs           = 0x30+3;
00086   */
00087 
00088   newProcess->tss.es           = 0x10;
00089   newProcess->tss.cs           = 0x08;
00090   newProcess->tss.ss           = 0x10;
00091   newProcess->tss.ds           = 0x10;
00092   newProcess->tss.fs           = 0x10;
00093   newProcess->tss.gs           = 0x10;
00094 
00095   newProcess->tss.ldt          = 0x18;
00096   newProcess->tss.trace_bitmap = 0x0000;
00097   newProcess->tss.io_map       = 0x8000;
00098   newProcess->oInfo.vmStart    = 0x6400000;
00099   
00100   newProcess->imageFd          = 0x0;
00101 
00102   /* Set up default stack for thread here filled with arg list 3 times */
00103   asm volatile(
00104     "pusha               \n"
00105     "movl   %%esp,%%ecx  \n"
00106     "movl   %1,%%eax     \n"
00107     "movl   %%eax,%%esp  \n"
00108     "pushl  %%ebx        \n"
00109     "pushl  %%ebx        \n"
00110     "pushl  %%ebx        \n"
00111     "movl   %%esp,%%eax  \n"
00112     "movl   %%eax,%1     \n"
00113     "movl   %%ecx,%%esp  \n"
00114     "popa                \n"
00115     :
00116     : "b" (arg),"m" (newProcess->tss.esp)
00117     );
00118 
00119   /* Put new thread into the READY state */
00120   sched_setStatus(newProcess->id,READY);
00121 
00122   /* Return with the new process ID */
00123   return((uInt32)newProcess);
00124   }
00125 
00126 /*****************************************************************************************
00127 
00128  Function: void execFile(char *file);
00129  Description: This Function Executes A Kile Into A New VM Space With Out
00130               Having To Fork
00131  Notes:
00132 
00133  07/30/02 - I Have Made Some Heavy Changes To This As Well As Fixed A Few
00134             Memory Leaks The Memory Allocated To Load The Binary Into Is
00135             Now Unmapped So It Can Be Used Again And Not Held Onto Until
00136             The Program Exits
00137 
00138  07/30/02 - Now I Have To Make A Better Memory Allocator So We Can Set Up
00139             The Freshly Allocated Pages With The Correct Permissions
00140 
00141 *****************************************************************************************/
00142 void execFile(char *file,int argc,char **argv,int console) {
00143 
00144   int        i         = 0x0;
00145   int        x         = 0x0;
00146   u_int32_t *tmp       = 0x0;
00147 
00148   fileDescriptor   *tmpFd         = 0x0;
00149   elfHeader        *binaryHeader  = 0x0;
00150   elfProgramHeader *programHeader = 0x0;
00151 
00152   /* Get A New Task For This Proccess */
00153   _current = schedNewTask();
00154   assert(_current);
00155   _current->gid  = 0x0;
00156   _current->uid  = 0x0;
00157   _current->term = tty_find(console);
00158   if (_current->term == 0x0)
00159     kprintf("Error: invalid console\n");
00160 
00161   /* Set tty ownership */
00162   _current->term->owner = _current->id;
00163 
00164   /* Now We Must Create A Virtual Space For This Proccess To Run In */
00165   _current->tss.cr3 = (uInt32)vmmCreateVirtualSpace(_current->id);
00166 
00167   /* To Better Load This Application We Will Switch Over To Its VM Space */
00168   asm volatile(
00169     "movl %0,%%eax          \n"
00170     "movl %%eax,%%cr3       \n"
00171     : : "d" ((uInt32 *)(_current->tss.cr3))
00172     );
00173 
00174   /* Lets Find The File */
00175   tmpFd = fopen(file,"r");
00176 
00177   /* If We Dont Find the File Return */
00178   if (tmpFd == 0x0) {
00179     kprintf("Exec Format Error: Binary File Not Executable.\n");
00180     fclose(tmpFd);
00181     return;
00182     }
00183   if (tmpFd->perms == 0x0) {
00184     kprintf("Exec Format Error: Binary File Not Executable.\n");
00185     fclose(tmpFd);
00186     return;
00187     }
00188 
00189   /* Load ELF Header */
00190   binaryHeader = (elfHeader *)kmalloc(sizeof(elfHeader));
00191 
00192 
00193   //kprintf(">a:%i:0x%X:0x%X<",sizeof(elfHeader),binaryHeader,tmpFd);
00194   fread(binaryHeader,sizeof(elfHeader),1,tmpFd);
00195 
00196 
00197   /* Check If App Is A Real Application */
00198   if ((binaryHeader->eIdent[1] != 'E') && (binaryHeader->eIdent[2] != 'L') && (binaryHeader->eIdent[3] != 'F')) {
00199     kprintf("Exec Format Error: Binary File Not Executable.\n");
00200     kfree(binaryHeader);
00201     fclose(tmpFd);
00202     return;
00203     }
00204   else if (binaryHeader->eType != 2) {
00205     kprintf("Exec Format Error: Binary File Not Executable.\n");
00206     kfree(binaryHeader);
00207     fclose(tmpFd);
00208     return;
00209     }
00210   else if (binaryHeader->eEntry == 0x300000) {
00211     kprintf("Exec Format Error: Binary File Not Executable.\n");
00212     kfree(binaryHeader);
00213     fclose(tmpFd);
00214     return;
00215     }
00216 
00217   /* Load The Program Header(s) */
00218   programHeader = (elfProgramHeader *)kmalloc(sizeof(elfProgramHeader)*binaryHeader->ePhnum);
00219   fseek(tmpFd,binaryHeader->ePhoff,0);
00220 
00221   //kprintf(">c:%i:0x%X:0x%X<",sizeof(elfProgramHeader)*binaryHeader->ePhnum,programHeader,tmpFd);
00222   fread(programHeader,(sizeof(elfProgramHeader)*binaryHeader->ePhnum),1,tmpFd);
00223   //kprintf(">d<");
00224 
00225   /* Loop Through The Header And Load Sections Which Need To Be Loaded */
00226   for (i=0;i<binaryHeader->ePhnum;i++) {
00227     if (programHeader[i].phType == 1) {
00228       /*
00229       Allocate Memory Im Going To Have To Make This Load Memory With Correct
00230       Settings so it helps us in the future
00231       */
00232       for (x = 0x0;x < (programHeader[i].phMemsz);x += 0x1000) {
00233         /* Make readonly and read/write !!! */
00234         if (vmm_remapPage(vmmFindFreePage(_current->id),((programHeader[i].phVaddr & 0xFFFFF000) + x),PAGE_DEFAULT) == 0x0)
00235           K_PANIC("Remap Page Failed");
00236 
00237         memset((void *)((programHeader[i].phVaddr & 0xFFFFF000) + x),0x0,0x1000);
00238         }
00239       _current->oInfo.vmStart = 0x80000000;
00240       _current->td.vm_daddr = (char *)(programHeader[i].phVaddr & 0xFFFFF000);
00241       /* Now Load Section To Memory */
00242       fseek(tmpFd,programHeader[i].phOffset,0);
00243       fread((void *)programHeader[i].phVaddr,programHeader[i].phFilesz,1,tmpFd);
00244       if ((programHeader[i].phFlags & 0x2) != 0x2) {
00245         kprintf("pH: [0x%X]\n",programHeader[i].phMemsz);
00246         for (x = 0x0;x < (programHeader[i].phMemsz);x += 0x1000) {
00247           if ((vmm_setPageAttributes((programHeader[i].phVaddr & 0xFFFFF000) + x,PAGE_PRESENT | PAGE_USER)) != 0x0)
00248             kpanic("Error: vmm_setPageAttributes failed, File: %s, Line: %i\n",__FILE__,__LINE__);
00249           }
00250         }
00251       }
00252     }
00253 
00254   /* Set Virtual Memory Start */
00255   _current->oInfo.vmStart = 0x80000000;
00256   _current->td.vm_daddr = (char *)(programHeader[i].phVaddr & 0xFFFFF000);
00257 
00258   /* Set Up Stack Space */
00259   for (x = 1;x < 100;x++) {
00260     vmm_remapPage(vmmFindFreePage(_current->id),STACK_ADDR - (x * 0x1000),PAGE_DEFAULT | PAGE_STACK);
00261     }
00262 
00263   /* Kernel Stack 0x2000 bytes long */
00264   vmm_remapPage(vmmFindFreePage(_current->id),0x5BC000,KERNEL_PAGE_DEFAULT | PAGE_STACK);
00265   vmm_remapPage(vmmFindFreePage(_current->id),0x5BB000,KERNEL_PAGE_DEFAULT | PAGE_STACK);
00266 
00267   /* Set All The Proper Information For The Task */
00268   _current->tss.back_link    = 0x0;
00269   _current->tss.esp0         = 0x5BC000;
00270   _current->tss.ss0          = 0x10;
00271   _current->tss.esp1         = 0x0;
00272   _current->tss.ss1          = 0x0;
00273   _current->tss.esp2         = 0x0;
00274   _current->tss.ss2          = 0x0;
00275   _current->tss.eip          = (long)binaryHeader->eEntry;
00276   _current->tss.eflags       = 0x206;
00277   _current->tss.esp          = STACK_ADDR - 12;
00278   _current->tss.ebp          = STACK_ADDR;
00279   _current->tss.esi          = 0x0;
00280   _current->tss.edi          = 0x0;
00281 
00282   /* Set these up to be ring 3 tasks */
00283   _current->tss.es           = 0x30+3;
00284   _current->tss.cs           = 0x28+3;
00285   _current->tss.ss           = 0x30+3;
00286   _current->tss.ds           = 0x30+3;
00287   _current->tss.fs           = 0x30+3;
00288   _current->tss.gs           = 0x30+3;
00289 
00290   _current->tss.ldt          = 0x18;
00291   _current->tss.trace_bitmap = 0x0000;
00292   _current->tss.io_map       = 0x8000;
00293 
00294   sched_setStatus(_current->id,READY);
00295 
00296   kfree(binaryHeader);
00297   kfree(programHeader);
00298   fclose(tmpFd);
00299 
00300   tmp = (uInt32 *)_current->tss.esp0 - 5;
00301   tmp[0] = binaryHeader->eEntry;
00302   tmp[3] = STACK_ADDR - 12;
00303 
00304   tmp = (uInt32 *)STACK_ADDR - 2;
00305 
00306   if (_current->id > 4)
00307   kprintf("argv[0]: [%s]\n",argv[0]);
00308   kprintf("argv: [0x%X]\n",argv);
00309   tmp[0] = (u_int32_t)argv;
00310   tmp[1] = (u_int32_t)argv;
00311 
00312 
00313   /* Switch Back To The Kernels VM Space */
00314   asm volatile(
00315     "movl %0,%%eax          \n"
00316     "movl %%eax,%%cr3       \n"
00317     : : "d" ((uInt32 *)(kernelPageDirectory))
00318     );
00319 
00320   /* Finally Return */
00321   return;
00322   }
00323 
00324 /*****************************************************************************************
00325 
00326  Function: void sysExec();
00327  Description: This Is The System Call To Execute A New Task
00328 
00329  Notes:
00330  04-22-03 - It Now Loads Sections Not The Full File
00331 
00332 *****************************************************************************************/
00333 void sysExec(char *file,char *ap) {
00334   int      i        = 0x0;
00335   int      x        = 0x0;
00336   int      argc     = 0x0;
00337   uInt32  *tmp      = 0x0;
00338   uInt32   ldAddr   = 0x0;
00339   uInt32   seg_size = 0x0;
00340   uInt32   seg_addr = 0x0;
00341   char    *interp   = 0x0;
00342   char   **argv     = 0x0;
00343   char   **argvNew  = 0x0;
00344   char    *args     = 0x0;
00345 
00346   fileDescriptor    *tmpFd         = 0x0;
00347   elfHeader         *binaryHeader  = 0x0;
00348   elfProgramHeader  *programHeader = 0x0;
00349   elfSectionHeader  *sectionHeader = 0x0;
00350   elfDynamic        *elfDynamicS   = 0x0;
00351   struct i386_frame *iFrame        = 0x0;
00352 
00353   tmpFd = fopen(file,"r");
00354   _current->imageFd = tmpFd;
00355   /* If We Dont Find the File Return */
00356   if (tmpFd == 0x0) {
00357     return;
00358     }
00359   if (tmpFd->perms == 0) {
00360     kprintf("Exec Format Error: Binary File Not Executable.\n");
00361     fclose(tmpFd);
00362     return;
00363     }
00364 
00365   /* Load ELF Header */
00366 
00367   if ((binaryHeader = (elfHeader *)kmalloc(sizeof(elfHeader))) == 0x0) 
00368     endTask(_current->id);
00369     fread(binaryHeader,sizeof(elfHeader),1,tmpFd);
00370     /* Set sectionHeader To Point To Loaded Binary To We Can Gather Info */
00371 
00372   /* Check If App Is A Real Application */
00373   if ((binaryHeader->eIdent[1] != 'E') && (binaryHeader->eIdent[2] != 'L') && (binaryHeader->eIdent[3] != 'F')) {
00374     kprintf("Exec Format Error: Binary File Not Executable.\n");
00375     kfree(binaryHeader);
00376     fclose(tmpFd);
00377 
00378     return;
00379     }
00380   else if (binaryHeader->eType != 2) {
00381     kprintf("Exec Format Error: Binary File Not Executable.\n");
00382     kfree(binaryHeader);
00383     fclose(tmpFd);
00384     return;
00385     }
00386   else if (binaryHeader->eEntry == 0x300000) {
00387     kprintf("Exec Format Error: Binary File Not Executable.\n");
00388     kfree(binaryHeader);
00389     fclose(tmpFd);
00390     return;
00391     }
00392 
00393   /* Load The Program Header(s) */
00394   if ((programHeader = (elfProgramHeader *)kmalloc(sizeof(elfProgramHeader)*binaryHeader->ePhnum)) == 0x0)
00395     endTask(_current->id);
00396 
00397   assert(programHeader);
00398   fseek(tmpFd,binaryHeader->ePhoff,0);
00399   fread(programHeader,(sizeof(elfProgramHeader)*binaryHeader->ePhnum),1,tmpFd);
00400 
00401   if ((sectionHeader = (elfSectionHeader *)kmalloc(sizeof(elfSectionHeader)*binaryHeader->eShnum)) == 0x0)
00402     endTask(_current->id);
00403 
00404   assert(sectionHeader);
00405   fseek(tmpFd,binaryHeader->eShoff,0);
00406   fread(sectionHeader,sizeof(elfSectionHeader)*binaryHeader->eShnum,1,tmpFd);
00407 
00408   /* Loop Through The Header And Load Sections Which Need To Be Loaded */
00409   for (i=0;i<binaryHeader->ePhnum;i++) {
00410     switch (programHeader[i].phType) {
00411       case PT_LOAD:
00412         seg_addr = trunc_page(programHeader[i].phVaddr);
00413         seg_size = round_page(programHeader[i].phMemsz + programHeader[i].phVaddr - seg_addr);
00414 
00415         /*
00416         Allocate Memory Im Going To Have To Make This Load Memory With Correct
00417         Settings so it helps us in the future
00418         */
00419         for (x = 0x0;x < (programHeader[i].phMemsz);x += 0x1000) {
00420           /* Make readonly and read/write !!! */
00421           if (vmm_remapPage(vmmFindFreePage(_current->id),((programHeader[i].phVaddr & 0xFFFFF000) + x),PAGE_DEFAULT) == 0x0)
00422             K_PANIC("Error: Remap Page Failed");
00423           memset((void *)((programHeader[i].phVaddr & 0xFFFFF000) + x),0x0,0x1000);
00424           }
00425 
00426         /* Now Load Section To Memory */
00427         fseek(tmpFd,programHeader[i].phOffset,0);
00428         fread((void *)programHeader[i].phVaddr,programHeader[i].phFilesz,1,tmpFd);
00429         if ((programHeader[i].phFlags & 0x2) != 0x2) {
00430           for (x = 0x0;x < (programHeader[i].phMemsz);x += 0x1000) {
00431             if ((vmm_setPageAttributes((programHeader[i].phVaddr & 0xFFFFF000) + x,PAGE_PRESENT | PAGE_USER)) != 0x0)
00432               kpanic("Error: vmm_setPageAttributes failed, File: %s,Line: %i\n",__FILE__,__LINE__);
00433             }
00434           }
00435         kprintf("setting daddr\n");
00436         if (binaryHeader->eEntry >= programHeader[i].phVaddr && binaryHeader->eEntry < (programHeader[i].phVaddr + programHeader[i].phMemsz)) {
00437           /* We're suposed to do something here? */
00438           }
00439         else {
00440           _current->td.vm_dsize = seg_size >> PAGE_SHIFT;
00441           _current->td.vm_daddr = (char *)seg_addr;
00442           }
00443 
00444         _current->oInfo.vmStart = ((programHeader[i].phVaddr & 0xFFFFF000) + 0xA900000);
00445         break;
00446       case PT_DYNAMIC:
00447         //newLoc = (char *)programHeader[i].phVaddr;
00448         elfDynamicS = (elfDynamic *)programHeader[i].phVaddr;
00449         fseek(tmpFd,programHeader[i].phOffset,0);
00450         fread((void *)programHeader[i].phVaddr,programHeader[i].phFilesz,1,tmpFd);
00451         break;
00452       case PT_INTERP:
00453         interp = (char *)kmalloc(programHeader[i].phFilesz);
00454         fseek(tmpFd,programHeader[i].phOffset,0);
00455         fread((void *)interp,programHeader[i].phFilesz,1,tmpFd);
00456         kprintf("Interp: [%s]\n",interp);
00457         ldAddr = ldEnable();
00458         break;
00459       default:
00460         break;
00461       }
00462     }
00463 
00464   /* What is this doing? 11/23/06 */
00465   if (elfDynamicS != 0x0) {
00466     for (i=0;i<12;i++) {
00467       if (elfDynamicS[i].dynVal == 0x3) {
00468         tmp = (uInt32 *)elfDynamicS[i].dynPtr;
00469         if (tmp == 0x0)
00470           kpanic("tmp: NULL\n");
00471         tmp[2] = (uInt32)ldAddr;
00472         tmp[1] = (uInt32)tmpFd;
00473         break;
00474         }
00475 /*
00476       else {
00477         kprintf("dyn_val: %i",elfDynamicS[i].dynVal);
00478         }
00479 */
00480       }
00481     }
00482           _current->td.vm_dsize = seg_size >> PAGE_SHIFT;
00483           _current->td.vm_daddr = (char *)seg_addr;
00484 
00485   argv = ap;
00486 
00487   if (argv[1] != 0x0) {
00488     argc = argv[0];
00489     args = (char *)vmmGetFreeVirtualPage(_current->id,1,VM_TASK);
00490 memset(args,0x0,0x1000);
00491     x = 0x0;
00492     argvNew = (char **)kmalloc(sizeof(char *) * argc);
00493     for (i = 0x0;i < argc;i++) {
00494       strcpy(args + x,argv[i + 1]);
00495       argvNew[i] = args + x;
00496       x += strlen(argv[i + 1]) + 1;
00497       //args[x] = '\0';
00498       //x++;
00499       }
00500     argv = argvNew;
00501     }
00502 
00504   vmm_cleanVirtualSpace(_current->td.vm_daddr + (_current->td.vm_dsize << PAGE_SIZE));
00505 
00506 
00508   iFrame = _current->tss.esp0 - sizeof(struct i386_frame);
00509   iFrame->ebp = STACK_ADDR;
00510   iFrame->eip = binaryHeader->eEntry;
00511   iFrame->user_esp = STACK_ADDR - 12;
00512 
00513   //if (_current->id > 3) {
00514 
00515     iFrame->user_esp = ((u_int32_t)STACK_ADDR) - (sizeof(u_int32_t) * (x + 1));
00516     tmp = iFrame->user_esp;
00517 
00519     tmp[0] = argc;
00520     for (i = 0;i < argc;i++) {
00521       tmp[i + 1] = argv[i];
00522       }
00523     //}
00524   //else {
00525     //tmp = (u_int32_t *)STACK_ADDR - 2;
00526     //tmp[0] = 0x1;
00527     //tmp[1] = 0x0;
00528     //tmp[1] = (u_int32_t)argv;
00529     //}
00530   kfree(argvNew);
00531  /* Now That We Relocated The Binary We Can Unmap And Free Header Info */
00532   kfree(binaryHeader);
00533   kfree(programHeader);
00534 
00535   return;
00536   }
00537 
00538 /***
00539  END
00540  ***/

Generated on Tue Dec 12 09:59:41 2006 for UbixOS V2 by  doxygen 1.4.7