diff options
| -rw-r--r-- | asm/int_syscall_handler.asm | 63 | ||||
| -rw-r--r-- | fs/elf.c | 102 | ||||
| -rw-r--r-- | fs/ext2.h | 1 | ||||
| -rw-r--r-- | kernel/kernel.c | 44 | ||||
| -rw-r--r-- | kernel/syscalls.c | 133 | ||||
| -rw-r--r-- | kernel/task.c | 23 | ||||
| -rw-r--r-- | kernel/vmem.c | 6 | ||||
| -rw-r--r-- | userspace/foolshell.c | 28 | ||||
| -rw-r--r-- | userspace/init.c | 17 |
9 files changed, 224 insertions, 193 deletions
diff --git a/asm/int_syscall_handler.asm b/asm/int_syscall_handler.asm index 89f152b..504d3e8 100644 --- a/asm/int_syscall_handler.asm +++ b/asm/int_syscall_handler.asm @@ -1,5 +1,7 @@ global int_syscall_handler [extern task_fork] +[extern task_exit] + [extern syscall_exit] [extern syscall_write] @@ -23,17 +25,19 @@ pid: dd 0x0 int_syscall_handler: + cmp eax, 72 je call_fork +cmp eax, 60 +je call_exit + cli push ebx push ecx push edx - cmp eax, 60 - je call_exit cmp eax, 61 je call_write @@ -78,12 +82,6 @@ je call_fork done: - mov ebx,eax - - mov al, 0x20 ;Port number AND command number to Acknowledge IRQ - out 0x20, al ;Acknowledge IRQ, so we keep getting interrupts - - mov eax,ebx done_blocking: @@ -98,6 +96,27 @@ done_blocking: iret ;Interrupt-Return +call_exit: + + cli + + pusha ;Push all standard registers + + mov esp, 0x7000 ;now put the stack outside of virtual memory in kernel space! + + push ebx ;Push pointer to all the stuff we just pushed + + call task_exit ;Call C code + + mov esp, eax ;Replace the stack with what the C code gave us + + popa ;Put the standard registers back + + sti + + iretd ;Interrupt-Return + ;;;; + call_fork: cli @@ -113,9 +132,6 @@ call_fork: mov esp, ebx ;Replace the stack with what the C code gave us - mov al, 0x20 ;Port number AND command number to Acknowledge IRQ - out 0x20, al ;Acknowledge IRQ, so we keep getting interrupts - popa ;Put the standard registers back @@ -166,9 +182,6 @@ call_sbrk: call syscall_sbrk jmp done -call_exit: - call syscall_exit - jmp done call_unhandled: call syscall_unhandled @@ -189,10 +202,24 @@ call_read: call_execve: - mov al, 0x20 ;Port number AND command number to Acknowledge IRQ - out 0x20, al ;Acknowledge IRQ, so we keep getting interrupts - sti + pusha + + mov eax,esp + mov esp,0x7000 + + push ebx + push ecx + push edx + mov ebx,eax call syscall_execve - jmp done_blocking + pop eax + pop eax + pop eax + + mov esp,ebx + + popa + + jmp done @@ -1,5 +1,8 @@ #include <stdint.h> +#include "ext2.h" +#include "lib/logger/log.h" +#define FOOLOS_MODULE_NAME "elf" #define EI_NIDENT 16 @@ -62,3 +65,102 @@ typedef struct { Elf32_Word p_align; } Elf32_Phdr; + +// returns elf entry point +uint32_t load_elf(char *name, uint32_t *alloc) +{ + + int inode_nr=ext2_filename_to_inode(EXT2_RAM_ADDRESS,name); + if(inode_nr<1)return 0; + + //TODO: load ELF binary and move this to own compilation unit + + //load binary + uint32_t vaddr=0x08048000; + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"loading"); + ext2_check(EXT2_RAM_ADDRESS); + ext2_inode_content(EXT2_RAM_ADDRESS,inode_nr,vaddr,0x100000); // load 1mb; + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"ELF File loaded to final destination."); + + Elf32_Ehdr *elf; + elf=vaddr; + + if(elf->e_ident[0]!=0x7f||elf->e_ident[1]!='E'||elf->e_ident[2]!='L'||elf->e_ident[3]!='F') + panic(FOOLOS_MODULE_NAME,"ELF mismatch!?"); + + + /* + log(FOOLOS_MODULE_NAME, FOOLOS_LOG_INFO, + "elf id: class=%d, data=%d, version=%d osabi=%d abiv=%d ", + elf->e_ident[4],elf->e_ident[5],elf->e_ident[6], + elf->e_ident[7],elf->e_ident[8]); + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf type: 0x%04x",elf->e_type); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf machine: 0x%04x",elf->e_machine); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf version: %d",elf->e_version); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf entry: 0x%08X",elf->e_entry); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-offset: 0x%08X",elf->e_phoff); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-offset: 0x%08X",elf->e_shoff); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf flags: 0x%08X",elf->e_flags); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf eh-size (bytes): %d",elf->e_ehsize); + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-ent-size(bytes): %d",elf->e_phentsize); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-num: %d",elf->e_phnum); + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-ent-size(byte): %d",elf->e_shentsize); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-num: %d",elf->e_shnum); + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-strndx: %d",elf->e_shstrndx); + */ + + // iterate over section headers + for(int phidx=0;phidx<elf->e_phnum;phidx++) + { + Elf32_Phdr *phdr=vaddr+elf->e_phoff+phidx*elf->e_phentsize; + + if(phidx==0) + { + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"text: 0x%08X-0x%08X",phdr->p_vaddr,phdr->p_vaddr+phdr->p_filesz); + } + + if(phidx==1) + { + + + /* + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"-- PROGRAMM HEADER %d --",phidx+1); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-type: %d",phdr->p_type); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-offset: 0x%08X",phdr->p_offset); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-vaddr: 0x%08X",phdr->p_vaddr); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-filesz: 0x%08X",phdr->p_filesz); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-memsz: 0x%08X",phdr->p_memsz); + */ + + + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"data: 0x%08X-0x%08X",phdr->p_vaddr,phdr->p_vaddr+phdr->p_filesz); + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"bss: 0x%08X-0x%08X",phdr->p_vaddr+phdr->p_filesz,phdr->p_vaddr+phdr->p_memsz); + + // let's copy the rw- data block + uint32_t *data=vaddr+phdr->p_offset; + for(uint32_t *addr=phdr->p_vaddr; addr<=phdr->p_vaddr+phdr->p_filesz; addr++) + { + *addr=*data; + data++; + } + + // let's zero init bss and set alloc (heap) just right after it! + for(uint32_t *addr=phdr->p_vaddr+phdr->p_filesz; addr<=phdr->p_vaddr+phdr->p_memsz; addr++) + { + *addr=0; + } + + *alloc=phdr->p_vaddr+phdr->p_memsz; + } + + + } + + return elf->e_entry; + +} @@ -1,4 +1,5 @@ #include <stdint.h> +#include "fs.h" int ext2_check(uint8_t *ram); int ext2_inode_content(char *ram,int inode_nr,uint8_t *ramdest,int max); int ext2_read_dir(uint8_t *ram, int inode_nr,fs_dirent *dirs,int max); diff --git a/kernel/kernel.c b/kernel/kernel.c index a0aaa7f..641c442 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -85,11 +85,19 @@ void kernel_main(uint32_t initial_stack, int mp) int_init(0x08); // + // Scan the PCI Bus + // + // We are interested in the E1000 Network Adapter in particular + // Its driver will be hopefully implemented one day ;) TODO + // + //pci_init(); + + + // // Gather Info about other processors. (APs) // ACPI or MP // // - /* smp_processors procdata; @@ -97,48 +105,22 @@ void kernel_main(uint32_t initial_stack, int mp) if(!mp_find(&procdata)) panic(FOOLOS_MODULE_NAME,"ACPI and MP search failed! I do not want to continue!"); */ - - task_init(); //; this will never return! - - - // load and run foolshell - // we will come back into the kernel only on interrupts... -/* - - static char *argv[]={"/bin/foolshell",NULL}; - static char *env[]={"PATH=/bin","PWD=/home/miguel","PS1=$ ",NULL}; - syscall_execve("/bin/foolshell",argv,env); - */ - - // // Start the other Processors (also before paging for some reason!) // //smp_log_procdata(&procdata); //smp_start_aps(&procdata,0x80000); // starts at 0x80000 // but it will be copied over mbr - - - // - // Scan the PCI Bus - // - // We are interested in the E1000 Network Adapter in particular - // Its driver will be hopefully implemented one day ;) TODO - // - //pci_init(); // // Initialize Multitasking // - // For now this starts three "tasks" which are scheduled - // round robin style. - // + + task_init(); //; this will never return! - //task_init(); + + - // Just hang around here, if its reached. - // we do our tasks anyway. on the next clock tick. - // while(1); } diff --git a/kernel/syscalls.c b/kernel/syscalls.c index 7bdb639..41d77ec 100644 --- a/kernel/syscalls.c +++ b/kernel/syscalls.c @@ -8,9 +8,8 @@ #include "kernel/console.h" #include "kernel/config.h" #include <sys/stat.h> -#include "fs/elf.c" -static uint32_t alloc; +static uint32_t alloc; // TODO: implement on a per process basis! int syscall_unhandled(int nr) { @@ -20,7 +19,6 @@ int syscall_unhandled(int nr) int syscall_lseek(int file,int ptr,int dir) { - #ifdef LOG_SYSCALLS log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"lseek (file=%d, ptr=%d, dir=%d)", file,ptr,dir); #endif @@ -31,6 +29,8 @@ int syscall_lseek(int file,int ptr,int dir) } + +// TODO: /dev/console or /dev/tty1 - /dev/ttyN int syscall_write(int file, char *buf, int len) { @@ -50,7 +50,7 @@ int syscall_write(int file, char *buf, int len) return len; } - +// TODO: /dev/kb int syscall_read(int file, char *buf, int len) { static bool eof=false; @@ -125,20 +125,8 @@ int syscall_wait(int *wait, int none1, int none2) #ifdef LOG_SYSCALLS log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"[%d] wait ",task_get_current_pid()); #endif -} - -int syscall_fork(int none, int none2, int none3) -{ - //int pid=add_task(esp,vmem_new_space_dir()); - int pid=task_get_current_pid(); - int esp=0; - - #ifdef LOG_SYSCALLS - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"[%d] fork: current esp: 0x%08X, pid: %d ",task_get_current_pid(),esp,pid); - #endif - - return 0; + panic(FOOLOS_MODULE_NAME,"unhandled syscall"); } int syscall_execve(char *name, char **argv1, char **env1) @@ -149,96 +137,12 @@ int syscall_execve(char *name, char **argv1, char **env1) #ifdef LOG_SYSCALLS log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"execve (name=0x%08X(%s), argvs=0x%08X, env=0x%08X)", name,name,argv,env); #endif - - int inode_nr=ext2_filename_to_inode(EXT2_RAM_ADDRESS,name); - if(inode_nr<1)return -1; - - //TODO: load ELF binary and move this to own compilation unit - //load binary - uint32_t vaddr=0x08048000; - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"loading"); - ext2_check(EXT2_RAM_ADDRESS); - ext2_inode_content(EXT2_RAM_ADDRESS,inode_nr,vaddr,0x100000); // load 1mb; - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"ELF File loaded to final destination."); - - Elf32_Ehdr *elf; - elf=vaddr; - - if(elf->e_ident[0]!=0x7f||elf->e_ident[1]!='E'||elf->e_ident[2]!='L'||elf->e_ident[3]!='F') - panic(FOOLOS_MODULE_NAME,"ELF mismatch!?"); - - - /* - log(FOOLOS_MODULE_NAME, FOOLOS_LOG_INFO, - "elf id: class=%d, data=%d, version=%d osabi=%d abiv=%d ", - elf->e_ident[4],elf->e_ident[5],elf->e_ident[6], - elf->e_ident[7],elf->e_ident[8]); - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf type: 0x%04x",elf->e_type); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf machine: 0x%04x",elf->e_machine); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf version: %d",elf->e_version); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf entry: 0x%08X",elf->e_entry); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-offset: 0x%08X",elf->e_phoff); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-offset: 0x%08X",elf->e_shoff); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf flags: 0x%08X",elf->e_flags); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf eh-size (bytes): %d",elf->e_ehsize); - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-ent-size(bytes): %d",elf->e_phentsize); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf ph-num: %d",elf->e_phnum); - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-ent-size(byte): %d",elf->e_shentsize); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-num: %d",elf->e_shnum); - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"elf sh-strndx: %d",elf->e_shstrndx); - */ - - // iterate over section headers - for(int phidx=0;phidx<elf->e_phnum;phidx++) - { - Elf32_Phdr *phdr=vaddr+elf->e_phoff+phidx*elf->e_phentsize; - - if(phidx==0) - { - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"text: 0x%08X-0x%08X",phdr->p_vaddr,phdr->p_vaddr+phdr->p_filesz); - } - - if(phidx==1) - { - - - /* - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"-- PROGRAMM HEADER %d --",phidx+1); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-type: %d",phdr->p_type); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-offset: 0x%08X",phdr->p_offset); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-vaddr: 0x%08X",phdr->p_vaddr); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-filesz: 0x%08X",phdr->p_filesz); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"p-memsz: 0x%08X",phdr->p_memsz); - */ - - - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"data: 0x%08X-0x%08X",phdr->p_vaddr,phdr->p_vaddr+phdr->p_filesz); - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"bss: 0x%08X-0x%08X",phdr->p_vaddr+phdr->p_filesz,phdr->p_vaddr+phdr->p_memsz); - - // let's copy the rw- data block - uint32_t *data=vaddr+phdr->p_offset; - for(uint32_t *addr=phdr->p_vaddr; addr<=phdr->p_vaddr+phdr->p_filesz; addr++) - { - *addr=*data; - data++; - } - - // let's zero init bss and set alloc (heap) just right after it! - for(uint32_t *addr=phdr->p_vaddr+phdr->p_filesz; addr<=phdr->p_vaddr+phdr->p_memsz; addr++) - { - *addr=0; - } - - alloc=phdr->p_vaddr+phdr->p_memsz; - } - - + uint32_t entry=load_elf(name,&alloc); + if(!entry) + { + log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"execve: bailing out!"); + return -1; // errror loading } @@ -261,8 +165,9 @@ int syscall_execve(char *name, char **argv1, char **env1) asm("push %0" :: "r" (env)); // push addr and return to it - asm("push %0"::"r"(elf->e_entry)); + asm("push %0"::"r"(entry)); + asm("sti"); asm("ret"); // this is never reached! @@ -276,7 +181,6 @@ int syscall_open(char *name, int flags, int mode) #endif panic(FOOLOS_MODULE_NAME,"unhandled syscall"); - } @@ -304,6 +208,7 @@ int syscall_isatty(int file,int none1,int none2) return 1; } +// TODO: per process basis! uint32_t syscall_sbrk(int incr, int none1, int none2) { uint32_t oldalloc=alloc; @@ -324,18 +229,6 @@ int syscall_exit(int ret, char **env, int none2) panic(FOOLOS_MODULE_NAME,"exit not supported yet" ); - /* - int i=0; - while(env[i]!=NULL) - { - log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"envvar %s (0x%08X)\n" ,env[i],env[i]); - i++; - } - - asm("mov $0x05bff,%esp"); // set stack pointer - static char *argv[]={"shell","--silent",NULL}; - syscall_execve("/bin/foolshell",argv,env); // start shell - */ } diff --git a/kernel/task.c b/kernel/task.c index 1780b77..6ea9b79 100644 --- a/kernel/task.c +++ b/kernel/task.c @@ -76,10 +76,31 @@ uint32_t task_switch_next(uint32_t oldesp) } +//TODO: free vmem too! +uint32_t task_exit(uint32_t oldesp) +{ + task_list[current_task].active=false; + + for(int i=0;i<MAX_TASKS;i++) + { + int pid=(current_task+1+i)%MAX_TASKS; // schedule round robin style + + if(task_list[pid].active) + { + // log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"switch from %d to pid: %d (0x%08X) vmem: %d ", current_task,pid,task_list[pid].esp,task_list[pid].vmem); + current_task=pid; + + vmem_set_dir(task_list[pid].vmem); + return task_list[pid].esp; + } + + } +} uint32_t task_fork(uint32_t oldesp) { - return add_task(oldesp,vmem_new_space_dir()); + int pid=add_task(oldesp,vmem_new_space_dir(task_list[current_task].vmem)); + return pid; } // init task (root of all other tasks / processes) // diff --git a/kernel/vmem.c b/kernel/vmem.c index 0a9e4b1..0cea40b 100644 --- a/kernel/vmem.c +++ b/kernel/vmem.c @@ -254,7 +254,7 @@ uint32_t vmem_alloc_block_at(uint32_t virt_addr) */ // vmem init and also copies all the shit for FORK -volatile int vmem_new_space_dir() +volatile int vmem_new_space_dir(int copy_dir) { x86_paging_disable(); @@ -319,7 +319,7 @@ volatile int vmem_new_space_dir() { ptable* table = (ptable*) pmmngr_alloc_block (); - pd_entry *oldentry= &(page_dirs[0]->m_entries[PAGE_DIRECTORY_INDEX(virt_addr)]); + pd_entry *oldentry= &(page_dirs[copy_dir]->m_entries[PAGE_DIRECTORY_INDEX(virt_addr)]); ptable* oldtable=pd_entry_get_frame(oldentry); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"oldtable at: 0x%08X",oldtable); @@ -381,7 +381,7 @@ void vmem_init() { //log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"init paging (vesa base: 0x%08x)",vesa_physbase); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"init paging"); - vmem_set_dir(vmem_new_space_dir()); + vmem_set_dir(vmem_new_space_dir(0)); x86_paging_enable(); } diff --git a/userspace/foolshell.c b/userspace/foolshell.c index e010c18..78ba327 100644 --- a/userspace/foolshell.c +++ b/userspace/foolshell.c @@ -16,12 +16,12 @@ void hello() " / __/ / /_/ / /_/ / / / /_/ /___/ / \n" " /_/ \\____/\\____/_/ \\____//____/ \n" " \n" - "Welcome to FoolShell v0.4 (Compiled on " __DATE__ " at " __TIME__ "\n" + "Welcome to FoolShell v0.5 (Compiled on " __DATE__ " at " __TIME__ "\n" "------------------------------------------------------------------\n\n" "Please type 'help' anytime, to show shell \"built-ins\" or execute \n" "user programms that are in you $PATH directory by simply typing \n" "their filenames. You can get additional information for many commands\n" - "by invking them with the --help or -h flag (e.g. ls --help) \n" + "by invoking them with the --help or -h flag (e.g. ls --help) \n" ); printf("Your $PATH is currently set to: %s\n\n",getenv("PATH")); @@ -211,20 +211,16 @@ int process(char *buf) } else { - char buf[256]; - sprintf(buf,"%s/%s",getenv("PATH"),token[0]); - - asm("mov $0x05bff,%esp"); // set stack pointer - execve(buf,token,environ); - asm("mov $0x07000,%esp"); // set stack pointer - - puts("foolshell: command not found"); - - asm("mov $0x05bff,%esp"); // set stack pointer - static char *argv[]={"shell","--silent",NULL}; - execve("/bin/foolshell",argv,environ); // start shell - - + int pid=fork(); + if(pid!=0) + { + printf("new task pid: %i \n",pid); + char buf[256]; + sprintf(buf,"%s/%s",getenv("PATH"),token[0]); + execve(buf,token,environ); + puts("foolshell: command not found"); + exit(1); + } } return 0; diff --git a/userspace/init.c b/userspace/init.c index d03d173..fd562a9 100644 --- a/userspace/init.c +++ b/userspace/init.c @@ -1,12 +1,21 @@ int main(int argc, char **argv) { - printf("Fool OS Init 0.1 \n"); - int pid=fork(); - while(1) + if(pid!=0) { - printf("I am pid: %i\n",pid); + printf("fool-init: forked child (pid: %i) spawning a Fools Shell\n",pid); + execve("/bin/foolshell",0,0); + + } + else + { + // TODO: wait for child and respawn shell if needed! + volatile int i=0; + while(1) + { + + } } return 0; } |
