/*************************************************************************** * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * As a special exception, if other files instantiate templates or use * * macros or inline functions from this file, or you compile this file * * and link it with other works to produce a work based on this file, * * this file does not by itself cause the resulting work to be covered * * by the GNU General Public License. However the source code for this * * file must still be made available in accordance with the GNU General * * Public License. This exception does not invalidate any other reasons * * why a work based on this file might be covered by the GNU General * * Public License. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ //Miosix kernel #include "kernel.h" #include "interfaces/portability.h" #include "interfaces/atomic_ops.h" #include "error.h" #include "logging.h" #include "arch_settings.h" #include "sync.h" #include "stage_2_boot.h" #include "process.h" #include "kernel/scheduler/scheduler.h" #include "stdlib_integration/libc_integration.h" #include #include #include #include /* Used by assembler context switch macros This variable is set by miosix::IRQfindNextThread in file kernel.cpp */ extern "C" { volatile unsigned int *ctxsave; } namespace miosix { //Global variables used by kernel.cpp. Those that are not static are also used //in portability.cpp and by the schedulers. //These variables MUST NOT be used outside kernel.cpp and portability.cpp volatile Thread *cur=NULL;///<\internal Thread currently running ///\internal True if there are threads in the DELETED status. Used by idle thread static volatile bool exist_deleted=false; static SleepData *sleeping_list=NULL;///<\internal list of sleeping threads static volatile long long tick=0;///<\internal Kernel tick ///\internal !=0 after pauseKernel(), ==0 after restartKernel() volatile int kernel_running=0; ///\internal true if a tick occurs while the kernel is paused volatile bool tick_skew=false; static bool kernel_started=false;///<\internal becomes true after startKernel. /// This is used by disableInterrupts() and enableInterrupts() to allow nested /// calls to these functions. static unsigned char interruptDisableNesting=0; #ifdef WITH_PROCESSES /// The proc field of the Thread class for kernel threads points to this object static ProcessBase *kernel=nullptr; #endif //WITH_PROCESSES /** * \internal * Idle thread. Created when the kernel is started, it phisically deallocates * memory for deleted threads, and puts the cpu in sleep mode. */ void *idleThread(void *argv) { (void) argv; for(;;) { if(exist_deleted) { PauseKernelLock lock; exist_deleted=false; Scheduler::PKremoveDeadThreads(); } #ifndef JTAG_DISABLE_SLEEP //JTAG debuggers lose communication with the device if it enters sleep //mode, so to use debugging it is necessary to remove this instruction miosix_private::sleepCpu(); #endif } return 0; //Just to avoid a compiler warning } void disableInterrupts() { //Before the kernel is started interrupts are disabled, //so disabling them again won't hurt miosix_private::doDisableInterrupts(); if(interruptDisableNesting==0xff) errorHandler(NESTING_OVERFLOW); interruptDisableNesting++; } void enableInterrupts() { if(interruptDisableNesting==0) { //Bad, enableInterrupts was called one time more than disableInterrupts errorHandler(DISABLE_INTERRUPTS_NESTING); } interruptDisableNesting--; if(interruptDisableNesting==0 && kernel_started==true) { miosix_private::doEnableInterrupts(); } } void pauseKernel() { int old=atomicAddExchange(&kernel_running,1); if(old>=0xff) errorHandler(NESTING_OVERFLOW); } void restartKernel() { int old=atomicAddExchange(&kernel_running,-1); if(old<=0) errorHandler(PAUSE_KERNEL_NESTING); //Check interruptDisableNesting to allow pauseKernel() while interrupts //are disabled with an InterruptDisableLock if(interruptDisableNesting==0) { //If we missed some tick yield immediately if(old==1 && tick_skew) { tick_skew=false; Thread::yield(); } } } bool areInterruptsEnabled() { return miosix_private::checkAreInterruptsEnabled(); } void startKernel() { #ifdef WITH_PROCESSES try { kernel=new ProcessBase; } catch(...) { errorHandler(OUT_OF_MEMORY); } #endif //WITH_PROCESSES // As a side effect this function allocates the idle thread and makes cur // point to it. It's probably been called many times during boot by the time // we get here, but we can't be sure auto *idle=Thread::IRQgetCurrentThread(); #ifdef WITH_PROCESSES // If the idle thread was allocated before startKernel(), then its proc // is nullptr. We can't move kernel=new ProcessBase; earlier than this // function, though idle->proc=kernel; #endif //WITH_PROCESSES // Create the idle and main thread Thread *main; main=Thread::doCreate(mainLoader,MAIN_STACK_SIZE,nullptr,Thread::DEFAULT,true); if(main==nullptr) errorHandler(OUT_OF_MEMORY); // Add them to the scheduler if(Scheduler::PKaddThread(main,MAIN_PRIORITY)==false) errorHandler(UNEXPECTED); // Idle thread needs to be set after main (see control_scheduler.cpp) Scheduler::IRQsetIdleThread(idle); // Make the C standard library use per-thread reeentrancy structure setCReentrancyCallback(Thread::getCReent); // Now kernel is started kernel_started=true; // Dispatch the task to the architecture-specific function miosix_private::IRQportableStartKernel(); } bool isKernelRunning() { return (kernel_running==0) && kernel_started; } long long getTick() { /* * Reading a volatile 64bit integer on a 32bit platform with interrupts * enabled is tricky because the operation is not atomic, so we need to * read it twice to see if we were interrupted in the middle of the read * operation. */ long long a,b; for(;;) { a=static_cast(tick); b=static_cast(tick); if(a==b) return a; } } /** * \internal * Used by Thread::sleep() to add a thread to sleeping list. The list is sorted * by the wakeup_time field to reduce time required to wake threads during * context switch. * Also sets thread SLEEP_FLAG. It is labeled IRQ not because it is meant to be * used inside an IRQ, but because interrupts must be disabled prior to calling * this function. */ void IRQaddToSleepingList(SleepData *x) { x->p->flags.IRQsetSleep(true); if((sleeping_list==NULL)||(x->wakeup_time <= sleeping_list->wakeup_time)) { x->next=sleeping_list; sleeping_list=x; } else { SleepData *cur=sleeping_list; for(;;) { if((cur->next==NULL)||(x->wakeup_time <= cur->next->wakeup_time)) { x->next=cur->next; cur->next=x; break; } cur=cur->next; } } } /** * \internal * Called @ every tick to check if it's time to wake some thread. * Also increases the system tick. * Takes care of clearing SLEEP_FLAG. * It is used by the kernel, and should not be used by end users. * \return true if some thread was woken. */ bool IRQwakeThreads() { tick++;//Increment tick bool result=false; for(;;) { if(sleeping_list==NULL) break;//If no item in list, return //Since list is sorted, if we don't need to wake the first element //we don't need to wake the other too if(tick != sleeping_list->wakeup_time) break; sleeping_list->p->flags.IRQsetSleep(false);//Wake thread sleeping_list=sleeping_list->next;//Remove from list result=true; } return result; } /* Memory layout for a thread |------------------------| | class Thread | |------------------------|<-- this | stack | | | | | V | |------------------------| | watermark | |------------------------|<-- base, watermark */ Thread *Thread::create(void *(*startfunc)(void *), unsigned int stacksize, Priority priority, void *argv, unsigned short options) { //Check to see if input parameters are valid if(priority.validate()==false || stacksizewatermark; thread->~Thread(); free(base); //Delete ALL thread memory return NULL; } } #ifdef SCHED_TYPE_EDF if(isKernelRunning()) yield(); //The new thread might have a closer deadline #endif //SCHED_TYPE_EDF return thread; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" Thread *Thread::create(void (*startfunc)(void *), unsigned int stacksize, Priority priority, void *argv, unsigned short options) { //Just call the other version with a cast. return Thread::create(reinterpret_cast(startfunc), stacksize,priority,argv,options); } #pragma GCC diagnostic pop void Thread::yield() { miosix_private::doYield(); } bool Thread::testTerminate() { //Just reading, no need for critical section return const_cast(cur)->flags.isDeleting(); } void Thread::sleep(unsigned int ms) { if(ms==0) return; //The SleepData variable has to be in scope till Thread::yield() returns //as IRQaddToSleepingList() makes it part of a linked list till the //thread wakes up (i.e: after Thread::yield() returns) SleepData d; //pauseKernel() here is not enough since even if the kernel is stopped //the tick isr will wake threads, modifying the sleeping_list { FastInterruptDisableLock lock; d.p=const_cast(cur); if(((ms*TICK_FREQ)/1000)>0) d.wakeup_time=getTick()+(ms*TICK_FREQ)/1000; //If tick resolution is too low, wait one tick else d.wakeup_time=getTick()+1; IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG } Thread::yield(); } void Thread::sleepUntil(long long absoluteTime) { //The SleepData variable has to be in scope till Thread::yield() returns //as IRQaddToSleepingList() makes it part of a linked list till the //thread wakes up (i.e: after Thread::yield() returns) SleepData d; //pauseKernel() here is not enough since even if the kernel is stopped //the tick isr will wake threads, modifying the sleeping_list { FastInterruptDisableLock lock; if(absoluteTime<=getTick()) return; //Wakeup time in the past, return d.p=const_cast(cur); d.wakeup_time=absoluteTime; IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG } Thread::yield(); } Thread *Thread::getCurrentThread() { Thread *result=const_cast(cur); if(result) return result; //This function must always return a pointer to a valid thread. The first //time this is called before the kernel is started, however, cur is nullptr. //thus we allocate the idle thread and return a pointer to that. return allocateIdleThread(); } bool Thread::exists(Thread *p) { if(p==NULL) return false; PauseKernelLock lock; return Scheduler::PKexists(p); } Priority Thread::getPriority() { return Scheduler::getPriority(this); } void Thread::setPriority(Priority pr) { if(pr.validate()==false) return; PauseKernelLock lock; Thread *current=getCurrentThread(); //If thread is locking at least one mutex if(current->mutexLocked!=0) { //savedPriority always changes, since when all mutexes are unlocked //setPriority() must become effective if(current->savedPriority==pr) return; current->savedPriority=pr; //Calculate new priority of thread, which is //max(savedPriority, inheritedPriority) Mutex *walk=current->mutexLocked; while(walk!=0) { if(walk->waiting.empty()==false) pr=std::max(pr,walk->waiting.front()->getPriority()); walk=walk->next; } } //If old priority == desired priority, nothing to do. if(pr==current->getPriority()) return; Scheduler::PKsetPriority(current,pr); #ifdef SCHED_TYPE_EDF if(isKernelRunning()) yield(); //Another thread might have a closer deadline #endif //SCHED_TYPE_EDF } void Thread::terminate() { //doing a read-modify-write operation on this->status, so pauseKernel is //not enough, we need to disable interrupts FastInterruptDisableLock lock; this->flags.IRQsetDeleting(); } void Thread::wait() { //pausing the kernel is not enough because of IRQwait and IRQwakeup { FastInterruptDisableLock lock; const_cast(cur)->flags.IRQsetWait(true); } Thread::yield(); //Return here after wakeup } void Thread::wakeup() { //pausing the kernel is not enough because of IRQwait and IRQwakeup { FastInterruptDisableLock lock; this->flags.IRQsetWait(false); } #ifdef SCHED_TYPE_EDF yield();//The other thread might have a closer deadline #endif //SCHED_TYPE_EDF } void Thread::PKwakeup() { //pausing the kernel is not enough because of IRQwait and IRQwakeup FastInterruptDisableLock lock; this->flags.IRQsetWait(false); } void Thread::detach() { FastInterruptDisableLock lock; this->flags.IRQsetDetached(); //we detached a terminated thread, so its memory needs to be deallocated if(this->flags.isDeletedJoin()) exist_deleted=true; //Corner case: detaching a thread, but somebody else already called join //on it. This makes join return false instead of deadlocking if(this->joinData.waitingForJoin!=NULL) { //joinData is an union, so its content can be an invalid thread //this happens if detaching a thread that has already terminated if(this->flags.isDeletedJoin()==false) { //Wake thread, or it might sleep forever this->joinData.waitingForJoin->flags.IRQsetJoinWait(false); } } } bool Thread::isDetached() const { return this->flags.isDetached(); } bool Thread::join(void** result) { { FastInterruptDisableLock dLock; if(this==Thread::IRQgetCurrentThread()) return false; if(Thread::IRQexists(this)==false) return false; if(this->flags.isDetached()) return false; if(this->flags.isDeletedJoin()==false) { //Another thread already called join on toJoin if(this->joinData.waitingForJoin!=NULL) return false; this->joinData.waitingForJoin=Thread::IRQgetCurrentThread(); for(;;) { //Wait Thread::IRQgetCurrentThread()->flags.IRQsetJoinWait(true); { FastInterruptEnableLock eLock(dLock); Thread::yield(); } if(Thread::IRQexists(this)==false) return false; if(this->flags.isDetached()) return false; if(this->flags.isDeletedJoin()) break; } } //Thread deleted, complete join procedure //Setting detached flag will make isDeleted() return true, //so its memory can be deallocated this->flags.IRQsetDetached(); if(result!=NULL) *result=this->joinData.result; } { PauseKernelLock lock; //Since there is surely one dead thread, deallocate it immediately //to free its memory as soon as possible Scheduler::PKremoveDeadThreads(); } return true; } Thread *Thread::IRQgetCurrentThread() { //Implementation is the same as getCurrentThread, but to keep a consistent //interface this method is duplicated Thread *result=const_cast(cur); if(result) return result; //This function must always return a pointer to a valid thread. The first //time this is called before the kernel is started, however, cur is nullptr. //thus we allocate the idle thread and return a pointer to that. return allocateIdleThread(); } Priority Thread::IRQgetPriority() { //Implementation is the same as getPriority, but to keep a consistent //interface this method is duplicated return Scheduler::IRQgetPriority(this); } void Thread::IRQwait() { const_cast(cur)->flags.IRQsetWait(true); } void Thread::IRQwakeup() { this->flags.IRQsetWait(false); } bool Thread::IRQexists(Thread* p) { if(p==NULL) return false; return Scheduler::PKexists(p); } const unsigned int *Thread::getStackBottom() { return getCurrentThread()->watermark+(WATERMARK_LEN/sizeof(unsigned int)); } int Thread::getStackSize() { return getCurrentThread()->stacksize; } Thread *Thread::doCreate(void*(*startfunc)(void*) , unsigned int stacksize, void* argv, unsigned short options, bool defaultReent) { unsigned int fullStackSize=WATERMARK_LEN+CTXSAVE_ON_STACK+stacksize; //Align fullStackSize to the platform required stack alignment fullStackSize+=CTXSAVE_STACK_ALIGNMENT-1; fullStackSize/=CTXSAVE_STACK_ALIGNMENT; fullStackSize*=CTXSAVE_STACK_ALIGNMENT; //Allocate memory for the thread, return if fail unsigned int *base=static_cast(malloc(sizeof(Thread)+ fullStackSize)); if(base==NULL) return NULL; //At the top of thread memory allocate the Thread class with placement new void *threadClass=base+(fullStackSize/sizeof(unsigned int)); Thread *thread=new (threadClass) Thread(base,stacksize,defaultReent); if(thread->cReentrancyData==nullptr) { thread->~Thread(); free(base); //Delete ALL thread memory return NULL; } //Fill watermark and stack memset(base, WATERMARK_FILL, WATERMARK_LEN); base+=WATERMARK_LEN/sizeof(unsigned int); memset(base, STACK_FILL, fullStackSize-WATERMARK_LEN); //On some architectures some registers are saved on the stack, therefore //initCtxsave *must* be called after filling the stack. miosix_private::initCtxsave(thread->ctxsave,startfunc, reinterpret_cast(thread),argv); if((options & JOINABLE)==0) thread->flags.IRQsetDetached(); return thread; } #ifdef WITH_PROCESSES void Thread::IRQhandleSvc(unsigned int svcNumber) { if(cur->proc==kernel) errorHandler(UNEXPECTED); if(svcNumber==SYS_USERSPACE) { const_cast(cur)->flags.IRQsetUserspace(true); ::ctxsave=cur->userCtxsave; //We know it's not the kernel, so the cast is safe static_cast(cur->proc)->mpu.IRQenable(); } else { const_cast(cur)->flags.IRQsetUserspace(false); ::ctxsave=cur->ctxsave; MPUConfiguration::IRQdisable(); } } bool Thread::IRQreportFault(const miosix_private::FaultData& fault) { if(const_cast(cur)->flags.isInUserspace()==false || cur->proc==kernel) return false; //We know it's not the kernel, so the cast is safe static_cast(cur->proc)->fault=fault; const_cast(cur)->flags.IRQsetUserspace(false); ::ctxsave=cur->ctxsave; MPUConfiguration::IRQdisable(); return true; } #endif //WITH_PROCESSES void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) { void *result=0; #ifdef __NO_EXCEPTIONS result=threadfunc(argv); #else //__NO_EXCEPTIONS try { result=threadfunc(argv); } catch(std::exception& e) { errorLog("***An exception propagated through a thread\n"); errorLog("what():%s\n",e.what()); } catch(...) { errorLog("***An exception propagated through a thread\n"); } #endif //__NO_EXCEPTIONS //Thread returned from its entry point, so delete it //Since the thread is running, it cannot be in the sleeping_list, so no need //to remove it from the list { FastInterruptDisableLock lock; const_cast(cur)->flags.IRQsetDeleted(); if(const_cast(cur)->flags.isDetached()==false) { //If thread is joinable, handle join if(cur->joinData.waitingForJoin!=NULL) { //Wake thread cur->joinData.waitingForJoin->flags.IRQsetJoinWait(false); } //Set result cur->joinData.result=result; } else { //If thread is detached, memory can be deallocated immediately exist_deleted=true; } } Thread::yield();//Since the thread is now deleted, yield immediately. //Will never reach here errorHandler(UNEXPECTED); } Thread *Thread::allocateIdleThread() { //NOTE: this function is only called once before the kernel is started, so //there are no concurrency issues, not even with interrupts // Create the idle and main thread auto *idle=Thread::doCreate(idleThread,STACK_IDLE,NULL,Thread::DEFAULT,true); if(idle==nullptr) errorHandler(OUT_OF_MEMORY); // cur must point to a valid thread, so we make it point to the the idle one cur=idle; return idle; } struct _reent *Thread::getCReent() { return getCurrentThread()->cReentrancyData; } #ifdef WITH_PROCESSES miosix_private::SyscallParameters Thread::switchToUserspace() { miosix_private::portableSwitchToUserspace(); miosix_private::SyscallParameters result(cur->userCtxsave); return result; } Thread *Thread::createUserspace(void *(*startfunc)(void *), void *argv, unsigned short options, Process *proc) { Thread *thread=doCreate(startfunc,SYSTEM_MODE_PROCESS_STACK_SIZE,argv, options,false); if(thread==NULL) return NULL; unsigned int *base=thread->watermark; try { thread->userCtxsave=new unsigned int[CTXSAVE_SIZE]; } catch(std::bad_alloc&) { thread->~Thread(); free(base); //Delete ALL thread memory return NULL;//Error } thread->proc=proc; thread->flags.IRQsetWait(true); //Thread is not yet ready //Add thread to thread list { //Handling the list of threads, critical section is required PauseKernelLock lock; if(Scheduler::PKaddThread(thread,MAIN_PRIORITY)==false) { //Reached limit on number of threads base=thread->watermark; thread->~Thread(); free(base); //Delete ALL thread memory return NULL; } } return thread; } void Thread::setupUserspaceContext(unsigned int entry, unsigned int *gotBase, unsigned int ramImageSize) { void *(*startfunc)(void*)=reinterpret_cast(entry); unsigned int *ep=gotBase+ramImageSize/sizeof(int); miosix_private::initCtxsave(cur->userCtxsave,startfunc,ep,0,gotBase); } #endif //WITH_PROCESSES Thread::Thread(unsigned int *watermark, unsigned int stacksize, bool defaultReent) : schedData(), flags(), savedPriority(0), mutexLocked(0), mutexWaiting(0), watermark(watermark), ctxsave(), stacksize(stacksize) { joinData.waitingForJoin=NULL; if(defaultReent) cReentrancyData=_GLOBAL_REENT; else { cReentrancyData=new _reent; if(cReentrancyData) _REENT_INIT_PTR(cReentrancyData); } #ifdef WITH_PROCESSES proc=kernel; userCtxsave=nullptr; #endif //WITH_PROCESSES } Thread::~Thread() { if(cReentrancyData && cReentrancyData!=_GLOBAL_REENT) { _reclaim_reent(cReentrancyData); delete cReentrancyData; } #ifdef WITH_PROCESSES if(userCtxsave) delete[] userCtxsave; #endif //WITH_PROCESSES } // // class ThreadFlags // void Thread::ThreadFlags::IRQsetWait(bool waiting) { if(waiting) flags |= WAIT; else flags &= ~WAIT; Scheduler::IRQwaitStatusHook(); } void Thread::ThreadFlags::IRQsetJoinWait(bool waiting) { if(waiting) flags |= WAIT_JOIN; else flags &= ~WAIT_JOIN; Scheduler::IRQwaitStatusHook(); } void Thread::ThreadFlags::IRQsetCondWait(bool waiting) { if(waiting) flags |= WAIT_COND; else flags &= ~WAIT_COND; Scheduler::IRQwaitStatusHook(); } void Thread::ThreadFlags::IRQsetSleep(bool sleeping) { if(sleeping) flags |= SLEEP; else flags &= ~SLEEP; Scheduler::IRQwaitStatusHook(); } void Thread::ThreadFlags::IRQsetDeleted() { flags |= DELETED; Scheduler::IRQwaitStatusHook(); } } //namespace miosix