00001 /* 00002 * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holders nor the names of 00014 * contributors may be used to endorse or promote products derived 00015 * from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS 00018 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00020 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE 00021 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00023 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 00024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 00025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00026 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 00027 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * For additional information see http://www.ethernut.de/ 00031 * 00032 */ 00033 00034 /* 00035 * $Log: context.c,v $ 00036 * Revision 1.10 2009/01/19 18:55:12 haraldkipp 00037 * Added stack checking code. 00038 * 00039 * Revision 1.9 2009/01/17 11:26:37 haraldkipp 00040 * Getting rid of two remaining BSD types in favor of stdint. 00041 * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'. 00042 * 00043 * Revision 1.8 2009/01/16 19:45:42 haraldkipp 00044 * All ARM code is now running in system mode. 00045 * 00046 * Revision 1.7 2008/08/11 06:59:14 haraldkipp 00047 * BSD types replaced by stdint types (feature request #1282721). 00048 * 00049 * Revision 1.6 2008/07/07 11:04:27 haraldkipp 00050 * Configurable ways of handling critical sections for ARM targets. 00051 * 00052 * Revision 1.5 2006/03/16 19:06:16 haraldkipp 00053 * Use link register to jump into thread and use dedicated routine to 00054 * jump into the idle thread. The way we did start the idle thread 00055 * before, jumping into the middle of NutThreadSwitch(), doesn't work 00056 * with optimized code. 00057 * 00058 * Revision 1.4 2006/03/16 15:25:00 haraldkipp 00059 * Changed human readable strings from u_char to char to stop GCC 4 from 00060 * nagging about signedness. 00061 * 00062 * Revision 1.3 2005/10/24 09:09:41 haraldkipp 00063 * Switch frame reduced. 00064 * NutThreadEntry included in the execution path. 00065 * The 'cc' globber had been removed from the context switching asm macro, 00066 * after Michael Fischer found out that this fixes a problem with optimized 00067 * compilation. Unfortunately, compiler optimized binaries still seem to 00068 * run unreliable. 00069 * 00070 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00071 * Major API documentation update. 00072 * 00073 * Revision 1.1 2005/07/26 18:10:48 haraldkipp 00074 * Moved from os/thread.c 00075 * 00076 * Revision 1.1 2005/05/27 17:16:40 drsung 00077 * Moved the file. 00078 * 00079 * Revision 1.4 2005/04/05 17:54:36 haraldkipp 00080 * Moved from user mode to system mode. Probably breaks the GBA port. 00081 * 00082 * Revision 1.3 2004/11/08 19:15:33 haraldkipp 00083 * Made assembly includes look nicer. 00084 * Changed mode from supervisory to user supervisory, which seems to work 00085 * with the GBA. 00086 * Skipped entry frame, because it simply confuses me. :-) 00087 * 00088 * Revision 1.2 2004/09/08 10:19:31 haraldkipp 00089 * Tyou's support for the ARM7 00090 * 00091 * Revision 1.1 2004/03/16 16:48:46 haraldkipp 00092 * Added Jan Dubiec's H8/300 port. 00093 * 00094 * Revision 1.2 2004/02/18 16:32:48 drsung 00095 * Bugfix in NutThreadCreate. Thanks to Mike Cornelius. 00096 * 00097 * Revision 1.1 2004/02/01 18:49:48 haraldkipp 00098 * Added CPU family support 00099 * 00100 */ 00101 00102 #include <cfg/os.h> 00103 00104 #include <string.h> 00105 00106 #include <sys/atom.h> 00107 #include <sys/heap.h> 00108 #include <sys/thread.h> 00109 00114 00115 00123 typedef struct { 00124 uint32_t csf_cpsr; 00125 uint32_t csf_r4; 00126 uint32_t csf_r5; 00127 uint32_t csf_r6; 00128 uint32_t csf_r7; 00129 uint32_t csf_r8; 00130 uint32_t csf_r9; 00131 uint32_t csf_r10; 00132 uint32_t csf_r11; /* AKA fp */ 00133 uint32_t csf_lr; 00134 } SWITCHFRAME; 00135 00141 typedef struct { 00142 uint32_t cef_r0; 00143 uint32_t cef_pc; 00144 } ENTERFRAME; 00145 00149 static void NutThreadEntry(void) __attribute__ ((naked)); 00150 void NutThreadEntry(void) 00151 { 00152 /* Load argument in r0 and jump to thread entry. */ 00153 asm volatile ("ldmfd sp!, {r0, lr}\n\tbx lr":::"r0", "lr"); 00154 } 00155 00156 00168 void NutThreadSwitch(void) __attribute__ ((naked)); 00169 void NutThreadSwitch(void) 00170 { 00171 /* Save CPU context. */ 00172 asm volatile ( /* */ 00173 "@ Save context\n\t" /* */ 00174 "stmfd sp!, {r4-r11, lr}\n\t" /* Save registers. */ 00175 "mrs r4, cpsr\n\t" /* Save status. */ 00176 "stmfd sp!, {r4}\n\t" /* */ 00177 "str sp, %0" /* Save stack pointer. */ 00178 ::"m" (runningThread->td_sp) /* */ 00179 ); 00180 00181 /* Select thread on top of the run queue. */ 00182 runningThread = runQueue; 00183 runningThread->td_state = TDS_RUNNING; 00184 00185 /* Restore context. */ 00186 __asm__ __volatile__( /* */ 00187 "@ Load context\n\t" /* */ 00188 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00189 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00190 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00191 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00192 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00193 "movs pc, lr" /* Restore status and return. */ 00194 ::"m"(runningThread->td_sp) /* */ 00195 ); 00196 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK) 00197 critical_nesting_level = 0; 00198 #endif 00199 } 00200 00221 HANDLE NutThreadCreate(char * name, void (*fn) (void *), void *arg, size_t stackSize) 00222 { 00223 uint8_t *threadMem; 00224 SWITCHFRAME *sf; 00225 ENTERFRAME *ef; 00226 NUTTHREADINFO *td; 00227 00228 /* 00229 * Allocate stack and thread info structure in one block. 00230 * We sill setup the following layout: 00231 * 00232 * Upper memory addresses. 00233 * 00234 * +--------------------+ 00235 * I I 00236 * I NUTTHREADINFO I 00237 * I I 00238 * td -> +-----+--------------+ <- Stack top 00239 * I I I 00240 * I T I ENTERFRAME I 00241 * I H I I 00242 * ef -> I R +--------------+ 00243 * I E I I ^ 00244 * I A I SWITCHFRAME I I 00245 * I D I I I pop moves up 00246 * sf -> I +--------------+ <- Initial stack pointer 00247 * I S I I I push moves down 00248 * I T I Application I I 00249 * I A I Stack I V 00250 * I C I I 00251 * I K I I 00252 * threadMem -> +-----+--------------+ <- Stack bottom 00253 * 00254 * Lower memory addresses. 00255 */ 00256 if ((threadMem = NutHeapAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) { 00257 return 0; 00258 } 00259 td = (NUTTHREADINFO *) (threadMem + stackSize); 00260 ef = (ENTERFRAME *) ((uintptr_t) td - sizeof(ENTERFRAME)); 00261 sf = (SWITCHFRAME *) ((uintptr_t) ef - sizeof(SWITCHFRAME)); 00262 00263 /* 00264 * Set predefined values at the stack bottom. May be used to detect 00265 * stack overflows. 00266 */ 00267 #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK) 00268 { 00269 uint32_t *fip = (uint32_t *)threadMem; 00270 while (fip < (uint32_t *)sf) { 00271 *fip++ = DEADBEEF; 00272 } 00273 } 00274 #else 00275 *((uint32_t *) threadMem) = DEADBEEF; 00276 *((uint32_t *) (threadMem + 4)) = DEADBEEF; 00277 *((uint32_t *) (threadMem + 8)) = DEADBEEF; 00278 *((uint32_t *) (threadMem + 12)) = DEADBEEF; 00279 #endif 00280 00281 /* 00282 * Setup the entry frame to simulate C function entry. 00283 */ 00284 ef->cef_pc = (uintptr_t) fn; 00285 ef->cef_r0 = (uintptr_t) arg; 00286 00287 /* 00288 * Setup the switch frame. 00289 */ 00290 sf->csf_lr = (uintptr_t) NutThreadEntry; 00291 sf->csf_cpsr = ARM_CPSR_I_BIT | ARM_CPSR_F_BIT | ARM_MODE_SYS; 00292 00293 /* 00294 * Initialize the thread info structure and insert it into the 00295 * thread list and the run queue. 00296 */ 00297 memcpy(td->td_name, name, sizeof(td->td_name) - 1); 00298 td->td_name[sizeof(td->td_name) - 1] = 0; 00299 td->td_state = TDS_READY; 00300 td->td_sp = (uintptr_t) sf; 00301 td->td_priority = 64; 00302 td->td_memory = threadMem; 00303 td->td_timer = 0; 00304 td->td_queue = 0; 00305 00306 NutEnterCritical(); 00307 td->td_next = nutThreadList; 00308 nutThreadList = td; 00309 NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue); 00310 00311 /* 00312 * If no thread is running, then this is the first thread ever 00313 * created. In Nut/OS, the idle thread is created first. 00314 */ 00315 if (runningThread == 0) { 00316 /* This will never return. */ 00317 runningThread = runQueue; 00318 runningThread->td_state = TDS_RUNNING; 00319 /* Restore context. */ 00320 __asm__ __volatile__( /* */ 00321 "@ Load context\n\t" /* */ 00322 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00323 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00324 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00325 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00326 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00327 "movs pc, lr" /* Restore status and return. */ 00328 ::"m"(runningThread->td_sp) /* */ 00329 ); 00330 } 00331 00332 /* 00333 * If current context is not in front of the run queue (highest 00334 * priority), then switch to the thread in front. 00335 */ 00336 if (runningThread != runQueue) { 00337 runningThread->td_state = TDS_READY; 00338 NutThreadSwitch(); 00339 } 00340 NutExitCritical(); 00341 00342 return td; 00343 } 00344