diff --git a/main.cpp b/main.cpp index bb91a21ed4e3736ac1dc44665e4df51e3e86f55e..74a32a0715507e3519ec176ee749f3501a28e5d3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,60 +1,12 @@ #include <cstdio> -#include <cassert> #include "miosix.h" -#include "kernel/elf_program.h" +#include "kernel/process.h" #include "app_template/prog3.h" using namespace std; using namespace miosix; -Thread *svcHandler=0; -Thread *blocked=0; - -void svcHandlerThread(void *) -{ - for(;;) - { - { - FastInterruptDisableLock dLock; - blocked=0; - for(;;) - { - if(blocked!=0) break; - Thread::getCurrentThread()->IRQwait(); - { - FastInterruptEnableLock eLock(dLock); - Thread::yield(); - } - } - } - - todo miosix_private::SyscallParameters sp=blocked->getSyscallParameters(); - if(sp.isValid()) - { - switch(sp.getSyscallId()) - { - case 1: - iprintf("Exit %d\n",sp.getFirstParameter()); - break; - case 2: -// iprintf("Write called %d %x %d\n", -// sp.getFirstParameter(), -// sp.getSecondParameter(), -// sp.getThirdParameter()); - sp.setReturnValue(write(sp.getFirstParameter(), - (const char*)sp.getSecondParameter(), - sp.getThirdParameter())); - blocked->wakeup(); - break; - } - } else { - iprintf("Unexpected invalid syscall\n"); - } - sp.invalidate(); - } -} - void ledThread(void *) { for(;;) @@ -69,15 +21,10 @@ void ledThread(void *) int main() { Thread::create(ledThread,STACK_MIN); - svcHandler=Thread::create(svcHandlerThread,2048); getchar(); ElfProgram prog(reinterpret_cast<const unsigned int*>(main_elf),main_elf_len); - ProcessImage pi; - pi.load(prog); - void *(*entry)(void*)=reinterpret_cast<void *(*)(void*)>(prog.getEntryPoint()); - Thread::createWithGotBase(entry,2048,1,0,0,pi.getProcessBasePointer()); - + Process::create(prog); for(;;) Thread::sleep(1000); } diff --git a/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability.cpp b/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability.cpp index b42f87d72985277e5a507c01487f0df5a81375d2..227961ccafa54dd8e45995a2a74fdc120f43df70 100644 --- a/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability.cpp +++ b/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability.cpp @@ -167,7 +167,7 @@ void IRQsystemReboot() } void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, - void *argv, unsigned int gotBase) + void *argv) { ctxsave[0]=(unsigned int)pc;// First function arg is passed in r0 ctxsave[1]=(unsigned int)argv; @@ -178,7 +178,7 @@ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, ctxsave[6]=0; ctxsave[7]=0; ctxsave[8]=0; - ctxsave[9]=gotBase; + ctxsave[9]=0; ctxsave[10]=0; ctxsave[11]=0; ctxsave[12]=0; @@ -189,6 +189,33 @@ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, ctxsave[16]=0x1f;//thread starts in system mode with irq and fiq enabled. } +#ifdef WITH_PROCESSES + +void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, + void *argv, unsigned int *gotBase) +{ + ctxsave[0]=(unsigned int)argv; + ctxsave[1]=0; + ctxsave[2]=0; + ctxsave[3]=0; + ctxsave[4]=0; + ctxsave[5]=0; + ctxsave[6]=0; + ctxsave[7]=0; + ctxsave[8]=0; + ctxsave[9]=(unsigned int)gotBase; + ctxsave[10]=0; + ctxsave[11]=0; + ctxsave[12]=0; + ctxsave[13]=(unsigned int)sp;//Initialize the thread's stack pointer + ctxsave[14]=0xffffffff;//threadLauncher never returns, so lr is not important + //Initialize the thread's program counter to the beginning of the entry point + ctxsave[15]=(unsigned int)pc; + ctxsave[16]=0x1f;//thread starts in system mode with irq and fiq enabled. +} + +#endif //WITH_PROCESSES + void IRQportableStartKernel() { PCONP|=(1<<1);//Enable TIMER0 diff --git a/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability_impl.h b/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability_impl.h index ffbfd86cfdb3bce060bb5d82bb0be3b20b7d8ed9..af14a0deee76b30a7d24f990b20c2df0b6dc474a 100644 --- a/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability_impl.h +++ b/miosix/arch/arm7_lpc2000/lpc2138_miosix_board/interfaces-impl/portability_impl.h @@ -181,7 +181,9 @@ namespace miosix_private { inline void doYield() { - asm volatile("swi 0"); + asm volatile("movs r3, #0\n\t" + "swi 0" + :::"r3"); } inline void doDisableInterrupts() diff --git a/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability.cpp index 8eb3a83c1e90b39d4a4425e36d6786eb0bad95be..d11081e9d0aebb3e4a409b7e8e8cbc23e5470f9b 100644 --- a/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability.cpp @@ -150,7 +150,7 @@ void IRQsystemReboot() } void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, - void *argv, unsigned int gotBase) + void *argv) { unsigned int *stackPtr=sp; stackPtr--; //Stack is full descending, so decrement first @@ -165,10 +165,32 @@ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, *stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0 ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp - ctxsave[6]=gotBase; //--> r9 + //leaving the content of r4-r11 uninitialized +} + +#ifdef WITH_PROCESSES + +void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, + void *argv, unsigned int *gotBase) +{ + unsigned int *stackPtr=sp; + stackPtr--; //Stack is full descending, so decrement first + *stackPtr=0x01000000; stackPtr--; //--> xPSR + *stackPtr=reinterpret_cast<unsigned long>(pc); stackPtr--; //--> pc + *stackPtr=0xffffffff; stackPtr--; //--> lr + *stackPtr=0; stackPtr--; //--> r12 + *stackPtr=0; stackPtr--; //--> r3 + *stackPtr=0; stackPtr--; //--> r2 + *stackPtr=0; stackPtr--; //--> r1 + *stackPtr=reinterpret_cast<unsigned long >(argv); //--> r0 + + ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp + ctxsave[6]=reinterpret_cast<unsigned long>(gotBase); //--> r9 //leaving the content of r4-r8,r10-r11 uninitialized } +#endif //WITH_PROCESSES + void IRQportableStartKernel() { //Enable fault handlers diff --git a/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability_impl.h b/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability_impl.h index c6686a632e24ecbad9eeab1f91bcf7a2b6216e23..9f46d1bce8f077cba6666b65eaf2705272ed5196 100644 --- a/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability_impl.h +++ b/miosix/arch/cortexM3_stm32/common/interfaces-impl/portability_impl.h @@ -102,7 +102,9 @@ namespace miosix_private { inline void doYield() { - asm volatile("svc 0"); + asm volatile("movs r3, #0\n\t" + "svc 0" + :::"r3"); } inline void doDisableInterrupts() diff --git a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability.cpp index 0148a5fb6d49c89c57e19283b7899f863cbfe990..a73ca68d92bbbb9ae2d430c4bab9d0e2edc261d8 100644 --- a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability.cpp @@ -85,9 +85,6 @@ void TIM3_IRQHandler() } #endif //SCHED_TYPE_CONTROL_BASED -extern miosix::Thread *svcHandler; -extern miosix::Thread *blocked; - namespace miosix_private { /** @@ -121,18 +118,11 @@ void ISR_yield() //it is a simple yield, otherwise pause the thread and wake the svcHandler unsigned int threadSp=ctxsave[0]; unsigned int *processStack=reinterpret_cast<unsigned int*>(threadSp); - if(processStack[3]!=0) - { - blocked=const_cast<miosix::Thread*>(miosix::cur); - blocked->IRQwait(); - if(svcHandler==0) miosix::errorHandler(miosix::UNEXPECTED); - svcHandler->IRQwakeup(); - } - #endif //WITH_PROCESSES - - //Note: this still has to be called even when r3!=0, since without this - //call the blocked->IRQwait() would not become effective + if(processStack[3]!=0) miosix::Thread::IRQswitchToKernelspace(); + else miosix::Scheduler::IRQfindNextThread(); + #else //WITH_PROCESSES miosix::Scheduler::IRQfindNextThread(); + #endif //WITH_PROCESSES } #ifdef SCHED_TYPE_CONTROL_BASED @@ -153,6 +143,8 @@ void ISR_auxTimer() void IRQstackOverflowCheck() { + //TODO: stack checking for userspace + if(const_cast<miosix::Thread*>(miosix::cur)->flags.isInUserspace()) return; const unsigned int watermarkSize=miosix::WATERMARK_LEN/sizeof(unsigned int); for(unsigned int i=0;i<watermarkSize;i++) { @@ -170,7 +162,7 @@ void IRQsystemReboot() } void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, - void *argv, unsigned int gotBase) + void *argv) { unsigned int *stackPtr=sp; stackPtr--; //Stack is full descending, so decrement first @@ -185,10 +177,32 @@ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, *stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0 ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp - ctxsave[6]=gotBase; //--> r9 + //leaving the content of r4-r11 uninitialized +} + +#ifdef WITH_PROCESSES + +void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, + void *argv, unsigned int *gotBase) +{ + unsigned int *stackPtr=sp; + stackPtr--; //Stack is full descending, so decrement first + *stackPtr=0x01000000; stackPtr--; //--> xPSR + *stackPtr=reinterpret_cast<unsigned long>(pc); stackPtr--; //--> pc + *stackPtr=0xffffffff; stackPtr--; //--> lr + *stackPtr=0; stackPtr--; //--> r12 + *stackPtr=0; stackPtr--; //--> r3 + *stackPtr=0; stackPtr--; //--> r2 + *stackPtr=0; stackPtr--; //--> r1 + *stackPtr=reinterpret_cast<unsigned long >(argv); //--> r0 + + ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp + ctxsave[6]=reinterpret_cast<unsigned long>(gotBase); //--> r9 //--> r9 //leaving the content of r4-r8,r10-r11 uninitialized } +#endif //WITH_PROCESSES + void IRQportableStartKernel() { //Enable fault handlers diff --git a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability_impl.h b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability_impl.h index 0e77afc6c87d4da6d00880782864c4ac3a043472..94b8f658dd1081f05f3f31cb27c7046dd62d7e81 100644 --- a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability_impl.h +++ b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/portability_impl.h @@ -104,7 +104,7 @@ inline void doYield() { asm volatile("movs r3, #0\n\t" "svc 0" - :::"r1"); + :::"r3"); } inline void doDisableInterrupts() diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp index 4c23fb5db59b365ed4298159db84f86507a0d593..89ca2132d189fe51bf41ebe65efe3c8ebda3d846 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp @@ -150,7 +150,7 @@ void IRQsystemReboot() } void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, - void *argv, unsigned int gotBase) + void *argv) { unsigned int *stackPtr=sp; stackPtr--; //Stack is full descending, so decrement first @@ -165,10 +165,32 @@ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, *stackPtr=reinterpret_cast<unsigned long >(pc); //--> r0 ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp - ctxsave[6]=gotBase; //--> r9 + //leaving the content of r4-r11 uninitialized +} + +#ifdef WITH_PROCESSES + +void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, + void *argv, unsigned int *gotBase) +{ + unsigned int *stackPtr=sp; + stackPtr--; //Stack is full descending, so decrement first + *stackPtr=0x01000000; stackPtr--; //--> xPSR + *stackPtr=reinterpret_cast<unsigned long>(pc); stackPtr--; //--> pc + *stackPtr=0xffffffff; stackPtr--; //--> lr + *stackPtr=0; stackPtr--; //--> r12 + *stackPtr=0; stackPtr--; //--> r3 + *stackPtr=0; stackPtr--; //--> r2 + *stackPtr=0; stackPtr--; //--> r1 + *stackPtr=reinterpret_cast<unsigned long >(argv); //--> r0 + + ctxsave[0]=reinterpret_cast<unsigned long>(stackPtr); //--> psp + ctxsave[6]=reinterpret_cast<unsigned long>(gotBase); //--> r9 //leaving the content of r4-r8,r10-r11 uninitialized } +#endif //WITH_PROCESSES + void IRQportableStartKernel() { //Enable fault handlers diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability_impl.h b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability_impl.h index 5fcc019b48c22748b3466d29368a89a918708327..e40061aa5df985f07b8f1e316cc554d79f25f1cb 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability_impl.h +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability_impl.h @@ -102,7 +102,9 @@ namespace miosix_private { inline void doYield() { - asm volatile("svc 0"); + asm volatile("movs r3, #0\n\t" + "svc 0" + :::"r3"); } inline void doDisableInterrupts() diff --git a/miosix/interfaces/portability.h b/miosix/interfaces/portability.h index 3c98a8fcf16ccf94b3ae7b50cca282b8a6465f9b..10e806f705d6a8528b753349fe17becfc46935af 100644 --- a/miosix/interfaces/portability.h +++ b/miosix/interfaces/portability.h @@ -95,11 +95,32 @@ inline void doYield(); * ctxsave * \param argv starting data passed to newly created thread, used to initialize * ctxsave + */ +void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, + void *argv); + +#ifdef WITH_PROCESSES + +/** + * \internal + * Initializes a ctxsave array when a thread is created. + * This version is to initialize the userspace context of processes. + * It is used by the kernel, and should not be used by end users. + * \param ctxsave a pointer to a field ctxsave inside a Thread class that need + * to be filled + * \param pc starting program counter of newly created thread, used to + * initialize ctxsave + * \param sp starting stack pointer of newly created thread, used to initialize + * ctxsave + * \param argv starting data passed to newly created thread, used to initialize + * ctxsave * \param gotBase base address of the global offset table, for userspace * processes */ void initCtxsave(unsigned int *ctxsave, void *(*pc)(void *), unsigned int *sp, - void *argv, unsigned int gotBase=0); + void *argv, unsigned int *gotBase); + +#endif //WITH_PROCESSES /** * \internal diff --git a/miosix/kernel/elf_program.cpp b/miosix/kernel/elf_program.cpp index 523052e22d57c89101614ee31ab5b9c7952ceb29..08cbfa059d7e9033049d71abd9216e577cb425d4 100644 --- a/miosix/kernel/elf_program.cpp +++ b/miosix/kernel/elf_program.cpp @@ -205,12 +205,13 @@ bool ElfProgram::validateDynamicSegment(const Elf32_Phdr *dynamic, // class ProcessImage // -void ProcessImage::load(ElfProgram& program) +void ProcessImage::load(const ElfProgram& program) { if(image) delete[] image; //TODO: add in elf file a field with the true image size image=new unsigned int[MAX_PROCESS_IMAGE_SIZE/4]; + size=MAX_PROCESS_IMAGE_SIZE; const unsigned int base=program.getElfBase(); const Elf32_Phdr *phdr=program.getProgramHeaderTable(); Elf32_Addr dtRel=0; diff --git a/miosix/kernel/elf_program.h b/miosix/kernel/elf_program.h index 87e3d116a570552d13c6a87e6307af348e1459ad..835d6a4dc26c25c4cdac957ce444158ac8a05e31 100644 --- a/miosix/kernel/elf_program.h +++ b/miosix/kernel/elf_program.h @@ -132,7 +132,7 @@ public: * the process, including copying .data, zeroing .bss and performing * relocations */ - void load(ElfProgram& program); + void load(const ElfProgram& program); /** * \return a pointer to the base of the program image diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index 1d12adbe02d368c6adb4274c643e20e600a72730..c02d7d0ef11399180f40465b2f00fa1266e7d7ca 100644 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -366,75 +366,6 @@ Thread *Thread::create(void (*startfunc)(void *), unsigned int stacksize, stacksize,priority,argv,options); } -Thread *Thread::createWithGotBase(void *(*startfunc)(void *), - unsigned int stacksize, - Priority priority, void *argv, - unsigned short options,unsigned int *gotBase) -{ - //Check to see if input parameters are valid - if(priority.validate()==false || stacksize<STACK_MIN) - { - errorHandler(INVALID_PARAMETERS); - return NULL; - } - //If stacksize is not divisible by 4, round it to a number divisible by 4 - stacksize &= ~0x3; - - //Allocate memory for the thread, return if fail - unsigned int *base; - Thread *thread; - #ifdef __NO_EXCEPTIONS - base=new int[(stacksize+WATERMARK_LEN+CTXSAVE_ON_STACK)/4]; - thread=new Thread(base,stacksize); - #else //__NO_EXCEPTIONS - try { - base=new unsigned int[(stacksize+WATERMARK_LEN+CTXSAVE_ON_STACK)/4]; - } catch(std::bad_alloc&) - { - errorHandler(OUT_OF_MEMORY); - return NULL;//Error - } - try { - thread=new Thread(base,stacksize); - } catch(std::bad_alloc&) - { - delete[] base; - errorHandler(OUT_OF_MEMORY); - return NULL;//Error - } - #endif //__NO_EXCEPTIONS - - //Fill watermark and stack - memset(base, WATERMARK_FILL, WATERMARK_LEN); - base+=WATERMARK_LEN/sizeof(unsigned int); - memset(base, STACK_FILL, stacksize); - - //On some architectures some registers are saved on the stack, therefore - //initCtxsave *must* be called after filling the stack. - unsigned int *topOfStack= - thread->watermark+(stacksize+WATERMARK_LEN+CTXSAVE_ON_STACK)/4; - miosix_private::initCtxsave(thread->ctxsave,startfunc,topOfStack,argv, - (unsigned int)gotBase); - - if((options & JOINABLE)==0) thread->flags.IRQsetDetached(); - - //Add thread to thread list - { - //Handling the list of threads, critical section is required - PauseKernelLock lock; - if(Scheduler::PKaddThread(thread,priority)==false) - { - //Reached limit on number of threads - delete thread; - return NULL; - } - } - #ifdef SCHED_TYPE_EDF - if(isKernelRunning()) yield(); //The new thread might have a closer deadline - #endif //SCHED_TYPE_EDF - return thread; -} - void Thread::yield() { miosix_private::doYield(); @@ -675,14 +606,16 @@ const int Thread::getStackSize() return cur->stacksize; } -miosix_private::SyscallParameters Thread::getSyscallParameters() +#ifdef WITH_PROCESSES + +void Thread::IRQswitchToKernelspace() { - unsigned int *context=const_cast<unsigned int*>(cur->ctxsave); - miosix_private::SyscallParameters result(context); - if(cur->pid==0) result.invalidate(); - return result; + const_cast<Thread*>(cur)->flags.IRQsetUserspace(false); + ::ctxsave=cur->ctxsave; } +#endif //WITH_PROCESSES + void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) { void *result=0; @@ -730,9 +663,95 @@ void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) errorHandler(UNEXPECTED); } +#ifdef WITH_PROCESSES + +Thread *Thread::PKcreateUserspace(void *(*startfunc)(void *), void *argv, + unsigned short options, pid_t pid) +{ + //Allocate memory for the thread, return if fail + unsigned int *base; + Thread *thread; + const int stackAlloc=(SYSTEM_MODE_PROCESS_STACK_SIZE+WATERMARK_LEN+ + CTXSAVE_ON_STACK)/4; + //TODO: move to RAII + try { + base=new unsigned int[stackAlloc]; + } catch(std::bad_alloc&) + { + errorHandler(OUT_OF_MEMORY); + return NULL;//Error + } + try { + thread=new Thread(base,SYSTEM_MODE_PROCESS_STACK_SIZE); + } catch(std::bad_alloc&) + { + delete[] base; + errorHandler(OUT_OF_MEMORY); + return NULL;//Error + } + try { + thread->userCtxsave=new unsigned int[CTXSAVE_SIZE]; + } catch(std::bad_alloc&) + { + delete[] thread; + delete[] base; + errorHandler(OUT_OF_MEMORY); + return NULL;//Error + } + + //Fill watermark and stack + memset(base, WATERMARK_FILL, WATERMARK_LEN); + base+=WATERMARK_LEN/sizeof(unsigned int); + memset(base, STACK_FILL, SYSTEM_MODE_PROCESS_STACK_SIZE); + + //On some architectures some registers are saved on the stack, therefore + //initCtxsave *must* be called after filling the stack. + unsigned int *topOfStack=thread->watermark+stackAlloc; + miosix_private::initCtxsave(thread->ctxsave,startfunc,topOfStack,argv); + + thread->pid=pid; + if((options & JOINABLE)==0) thread->flags.IRQsetDetached(); + + //Add thread to thread list + if(Scheduler::PKaddThread(thread,MAIN_PRIORITY)==false) + { + //Reached limit on number of threads + delete thread; + return NULL; + } + + return thread; +} + +void Thread::setupUserspaceContext(unsigned int entry, unsigned int *gotBase, + unsigned int *stackTop) +{ + void *(*startfunc)(void*)=reinterpret_cast<void *(*)(void*)>(entry); + miosix_private::initCtxsave(cur->userCtxsave,startfunc,stackTop,0,gotBase); +} + +miosix_private::SyscallParameters Thread::switchToUserspace() +{ + { + FastInterruptDisableLock dLock; + const_cast<Thread*>(cur)->flags.IRQsetUserspace(true); + } + Thread::yield(); //TODO: use a syscall to perform the continuation + if(const_cast<Thread*>(cur)->flags.isInUserspace()==true) + errorHandler(UNEXPECTED); + miosix_private::SyscallParameters result(cur->userCtxsave); + if(cur->pid==0) result.invalidate(); + return result; +} + +#endif //WITH_PROCESSES + Thread::~Thread() { - if(flags.hasExternStack()==false) delete[] watermark; + delete[] watermark; + #ifdef WITH_PROCESSES + if(userCtxsave) delete[] userCtxsave; + #endif //WITH_PROCESSES } // diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index e746ccfe2a83bcc15c5c214fa34c3076ef661341..3f33bccc3ab8c51a8c77f6f7ef0a7f7b542071bd 100644 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -693,10 +693,14 @@ public: static const int getStackSize(); #ifdef WITH_PROCESSES + /** - * \return the syscall parameters + * TODO: make private! + * Can only be called inside an IRQ, its use is to switch a userspace thread + * to kernelspace to perform a system call. */ - static miosix_private::SyscallParameters getSyscallParameters(); + static void IRQswitchToKernelspace(); + #endif //WITH_PROCESSES private: @@ -710,46 +714,45 @@ private: /** * Constructor, sets flags to default. */ - ThreadFlags(bool externStack=false) : - flags(externStack ? EXTERN_STACK : 0) {} + ThreadFlags() : flags(0) {} /** * Set the wait flag of the thread. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. * \param waiting if true the flag will be set, otherwise cleared */ void IRQsetWait(bool waiting); /** * Set the wait_join flag of the thread. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. * \param waiting if true the flag will be set, otherwise cleared */ void IRQsetJoinWait(bool waiting); /** * Set wait_cond flag of the thread. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. * \param waiting if true the flag will be set, otherwise cleared */ void IRQsetCondWait(bool waiting); /** * Set the sleep flag of the thread. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. * \param sleeping if true the flag will be set, otherwise cleared */ void IRQsetSleep(bool sleeping); /** * Set the deleted flag of the thread. This flag can't be cleared. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. */ void IRQsetDeleted(); /** * Set the sleep flag of the thread. This flag can't be cleared. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. */ void IRQsetDeleting() { @@ -758,12 +761,22 @@ private: /** * Set the detached flag. This flag can't be cleared. - * Can only be called with interrupts enabled or within an interrupt. + * Can only be called with interrupts disabled or within an interrupt. */ void IRQsetDetached() { flags |= DETACHED; } + + /** + * Set the userspace flag of the thread. + * Can only be called with interrupts disabled or within an interrupt. + * \param sleeping if true the flag will be set, otherwise cleared + */ + void IRQsetUserspace(bool userspace) + { + if(userspace) flags |= USERSPACE; else flags &= ~USERSPACE; + } /** * \return true if the wait flag is set @@ -811,11 +824,6 @@ private: */ bool isWaitingCond() const { return flags & WAIT_COND; } - /** - * \return true if stack is handled separately and must not be deleted - */ - bool hasExternStack() const { return flags & EXTERN_STACK; } - /** * \return true if the thread is running unprivileged inside a process. * Only threads whose pid is not zero can run in userspace @@ -847,31 +855,43 @@ private: ///\internal Thread is waiting on a condition variable static const unsigned int WAIT_COND=1<<6; - ///\internal Thread stack is handled separately and must not be freed - ///at thread termination - static const unsigned int EXTERN_STACK=1<<7; - ///\internal Thread is running in userspace static const unsigned int USERSPACE=1<<7; unsigned short flags;///<\internal flags are stored here }; + #ifdef WITH_PROCESSES + + /** + * Create a thread to be used inside a process. + * Can only be called when the kernel is paused + * \param startfunc entry point + * \param argv parameter to be passed to the entry point + * \param options thread options + * \param pid process' pid + */ + static Thread *PKcreateUserspace(void *(*startfunc)(void *), + void *argv, unsigned short options, pid_t pid); + /** - * - * \param startfunc - * \param stacksize - * \param priority - * \param argv - * \param options - * \param gotBase - * \return + * Setup the userspace context of the thread, so that it can be later + * switched to userspace. Must be called only once for each thread instance + * Can only be called when the kernel is paused + * \param entry userspace entry point + * \param gotBase base address of the GOT, also corresponding to the start + * of the RAM image of the process + * \param stackTop top of userspace stack */ - static Thread *PKcreate(void *(*startfunc)(void *), - unsigned int stacksize, - Priority priority, void *argv, - unsigned short options,unsigned int *gotBase, - pid_t pid, unsigned int entry, unsigned int *base); + static void setupUserspaceContext(unsigned int entry, unsigned int *gotBase, + unsigned int *stackTop); + + /** + * \return the syscall parameters + */ + static miosix_private::SyscallParameters switchToUserspace(); + + #endif //WITH_PROCESSES /** * Constructor, initializes thread data. @@ -884,6 +904,10 @@ private: watermark(watermark), ctxsave(), stacksize(stacksize) { joinData.waitingForJoin=NULL; + #ifdef WITH_PROCESSES + pid=0; + userCtxsave=0; + #endif //WITH_PROCESSES } /** @@ -951,8 +975,7 @@ private: friend void startKernel(); //Needs threadLauncher friend void miosix_private::initCtxsave(unsigned int *ctxsave, - void *(*pc)(void *), unsigned int *sp, void *argv, - unsigned int gotBase); + void *(*pc)(void *), unsigned int *sp, void *argv); //Needs access to priority, savedPriority, mutexLocked and flags. friend class Mutex; //Needs access to flags diff --git a/miosix/kernel/process.cpp b/miosix/kernel/process.cpp index 554d9ba48999466374027857d2c51fa5436646e3..49f381031d8c1849c4eb54880910b38fee32f28d 100644 --- a/miosix/kernel/process.cpp +++ b/miosix/kernel/process.cpp @@ -26,6 +26,8 @@ ***************************************************************************/ #include <stdexcept> +#include <memory> +#include <cstdio> #include "process.h" using namespace std; @@ -40,54 +42,95 @@ namespace miosix { Process *Process::create(const ElfProgram& program) { - //Loading the process outside the PKlock as relocation take time - Process *proc=new Process; - proc->image.load(program); + //Loading the process outside the PKlock as relocation takes time + auto_ptr<Process> proc(new Process(program)); { PauseKernelLock dLock; pid_t pid=PKgetNewPid(); - try { - processes[pid]=proc; - } catch(...) { - delete proc; - throw; + processes[pid]=proc.get(); + Thread *thr=Thread::PKcreateUserspace(Process::start,0, + Thread::JOINABLE,pid); + if(thr==0) + { + processes.erase(pid); + throw runtime_error("Thread creation failed"); } - Thread *thr=Thread::PKcreate(Process::start, - SYSTEM_MODE_PROCESS_STACK_SIZE,MAIN_PRIORITY,0,Thread::JOINABLE,pid, - program.getEntryPoint(),proc->image.getProcessBasePointer()); - if(thr==0) throw runtime_error("Thread creation failed"); //Cannot throw bad_alloc due to the reserve in Process's constructor. //This ensures we will never be in the uncomfortable situation where a //thread has already been created but there's no memory to list it //among the threads of a process proc->threads.push_back(thr); } + #ifdef SCHED_TYPE_EDF + //The new thread might have a closer deadline + if(isKernelRunning()) Thread::yield(); + #endif //SCHED_TYPE_EDF + return proc.release(); //Do not delete the pointer } -Process::Process() +Process::Process(const ElfProgram& program) : program(program) { //This is required so that bad_alloc can never be thrown when the first //thread of the process will be stored in this vector threads.reserve(1); + //Done here so if not enough memory the new process is not even created + image.load(program); } void *Process::start(void *argv) { - //TODO + Thread *thr=Thread::getCurrentThread(); + map<pid_t,Process*>::iterator it=processes.find(thr->pid); + if(it==processes.end()) errorHandler(UNEXPECTED); + Process *proc=it->second; + Thread::setupUserspaceContext( + proc->program.getEntryPoint(),proc->image.getProcessBasePointer(), + proc->image.getProcessBasePointer()+proc->image.getProcessImageSize()); + bool running=true; + do { + miosix_private::SyscallParameters sp=Thread::switchToUserspace(); + if(sp.isValid()) + { + switch(sp.getSyscallId()) + { + case 1: + iprintf("Exit %d\n",sp.getFirstParameter()); + running=false; + break; + case 2: + //FIXME: check that the pointer belongs to the process + sp.setReturnValue(write(sp.getFirstParameter(), + reinterpret_cast<const char*>(sp.getSecondParameter()), + sp.getThirdParameter())); + break; + default: + iprintf("Unexpected invalid syscall\n"); + running=false; + break; + } + } + if(Thread::testTerminate()) + { + running=false; + } + } while(running); + //TODO: handle process termination + return 0; } pid_t Process::PKgetNewPid() { for(;;pidCounter++) { + if(pidCounter<0) pidCounter=1; if(pidCounter==0) continue; //Zero is not a valid pid - map<pid_t,Process *>::iterator it=processes.find(pidCounter); + map<pid_t,Process*>::iterator it=processes.find(pidCounter); if(it!=processes.end()) continue; //Pid number already used return pidCounter++; } } -map<pid_t,Process *> Process::processes; +map<pid_t,Process*> Process::processes; pid_t Process::pidCounter=1; } //namespace miosix diff --git a/miosix/kernel/process.h b/miosix/kernel/process.h index 0c70b7e49ce0d55a9a0964d9c0a17ece55374f3a..51b9fe16da8a332d557e40e0bb8661cf84adefbc 100644 --- a/miosix/kernel/process.h +++ b/miosix/kernel/process.h @@ -39,23 +39,46 @@ namespace miosix { +/** + * Process class, allows to create and handle processes + */ class Process { public: - + /** + * Create a new process + * \param program Program that the process will execute + * \return a pointer to the newly created process + * \throws std::exception or a subclass in case of errors, including + * not emough memory to spawn the process + */ static Process *create(const ElfProgram& program); private: + Process(const Process&); + Process& operator= (const Process&); - Process(); + /** + * Constructor + * \param program program that will be executed by the process + */ + Process(const ElfProgram& program); + /** + * Contains the process' main loop. + * \param argv ignored parameter + * \return null + */ static void *start(void *argv); + /** + * \return an unique pid that is not zero and is not already in use in the + * system, used to assign a pid to a new process.<br> + * Must be called when the kernel is paused to avoid race conditions. + */ static pid_t PKgetNewPid(); - Process(const Process&); - Process& operator= (const Process&); - + ElfProgram program; ///<The program that is running inside the process ProcessImage image; ///<The RAM image of a process std::vector<Thread *> threads; ///<Threads that belong to the process diff --git a/miosix/kernel/scheduler/control/control_scheduler.cpp b/miosix/kernel/scheduler/control/control_scheduler.cpp index 9effc279f8454a93a039245171f64b3f6270730b..e909985099bdcea0641eec30ceee44c36dd56e66 100644 --- a/miosix/kernel/scheduler/control/control_scheduler.cpp +++ b/miosix/kernel/scheduler/control/control_scheduler.cpp @@ -209,7 +209,13 @@ void ControlScheduler::IRQfindNextThread() { //Found a READY thread, so run this one cur=curInRound; - ctxsave=cur->ctxsave; + #ifdef WITH_PROCESSES + if(const_cast<Thread*>(cur)->flags.isInUserspace()==false) + ctxsave=temp->ctxsave; + else ctxsave=temp->userCtxsave; + #else //WITH_PROCESSES + ctxsave=temp->ctxsave; + #endif //WITH_PROCESSES miosix_private::AuxiliaryTimer::IRQsetValue( curInRound->schedData.bo/multFactor); return; diff --git a/miosix/kernel/scheduler/edf/edf_scheduler.cpp b/miosix/kernel/scheduler/edf/edf_scheduler.cpp index d115a44d34bd559b275c5234c7e4a0d450f49e4d..af6a4f56477f07ed2e579cdeb89cfafb114643cf 100644 --- a/miosix/kernel/scheduler/edf/edf_scheduler.cpp +++ b/miosix/kernel/scheduler/edf/edf_scheduler.cpp @@ -111,7 +111,13 @@ void EDFScheduler::IRQfindNextThread() if(walk->flags.isReady()) { cur=walk; - ctxsave=cur->ctxsave; + #ifdef WITH_PROCESSES + if(const_cast<Thread*>(cur)->flags.isInUserspace()==false) + ctxsave=temp->ctxsave; + else ctxsave=temp->userCtxsave; + #else //WITH_PROCESSES + ctxsave=temp->ctxsave; + #endif //WITH_PROCESSES return; } walk=walk->schedData.next; diff --git a/miosix/kernel/scheduler/priority/priority_scheduler.cpp b/miosix/kernel/scheduler/priority/priority_scheduler.cpp index da6eddae94578087900a71c290d0b1df947c9050..8f39ab965491f94b5d91615b597296a669d3a266 100644 --- a/miosix/kernel/scheduler/priority/priority_scheduler.cpp +++ b/miosix/kernel/scheduler/priority/priority_scheduler.cpp @@ -200,7 +200,13 @@ void PriorityScheduler::IRQfindNextThread() { //Found a READY thread, so run this one cur=temp; + #ifdef WITH_PROCESSES + if(const_cast<Thread*>(cur)->flags.isInUserspace()==false) + ctxsave=temp->ctxsave; + else ctxsave=temp->userCtxsave; + #else //WITH_PROCESSES ctxsave=temp->ctxsave; + #endif //WITH_PROCESSES //Rotate to next thread so that next time the list is walked //a different thread, if available, will be chosen first thread_list[i]=temp;