]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/lib/tosthreads/lib/tinyld/DynamicLoaderP.nc
Merge TinyOS 2.1.1 into master.
[tinyos-2.x.git] / tos / lib / tosthreads / lib / tinyld / DynamicLoaderP.nc
diff --git a/tos/lib/tosthreads/lib/tinyld/DynamicLoaderP.nc b/tos/lib/tosthreads/lib/tinyld/DynamicLoaderP.nc
new file mode 100755 (executable)
index 0000000..37095b2
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2008 Johns Hopkins University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written
+ * agreement is hereby granted, provided that the above copyright
+ * notice, the (updated) modification history and the author appear in
+ * all copies of this source code.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA,
+ * OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * @author Chieh-Jan Mike Liang <cliang4@cs.jhu.edu>
+ * @author Razvan Musaloiu-E. <razvanm@cs.jhu.edu>
+ */
+#include SLCS_TYPES_FILE
+#include "thread.h"
+
+module DynamicLoaderP
+{
+  provides {
+    interface Init;
+    interface DynamicLoader;
+  }
+  
+  uses {
+    interface Leds;
+    interface BlockRead as ImageRead[uint8_t id];
+    interface DynamicThread;
+    interface ThreadNotification[uint8_t id];
+    interface ThreadScheduler;
+    interface ReferenceCounter;
+    interface PMManager;
+#ifndef DISABLE_LOADER_USERBUTTON
+    interface UserButton;
+#endif
+  }
+}
+
+implementation
+{ 
+  uint8_t *code;   // Points to first byte of the program code in the internal flash
+  uint8_t *tablesMemory;
+  void *gVarMemory;
+  struct prog_desc prog_desc;
+  init_block_t *proc;
+  
+  uint16_t codePtr;   // Records what code has been copied to the internal flash
+  uint16_t nextAddr;
+  uint8_t *nextTask_chain;   // Used to update the next patching address in a chain
+  
+  uint8_t readSource;   // Loads from flash or memory
+  uint16_t readSourceOffset;   // If loading from memory, then this is effectively the passed-in memory address  
+  error_t retError = FAIL;
+  tosthread_t handler;
+  uint16_t threadCodeSizes[TOSTHREAD_MAX_NUM_THREADS];
+  uint16_t codeFirstAddrs[TOSTHREAD_MAX_NUM_THREADS];
+  
+  async event void ThreadNotification.justCreated[uint8_t id]()
+  {
+    thread_t *t = call ThreadScheduler.threadInfo(id);
+    if(t->init_block != NULL) {
+      call ReferenceCounter.increment(&(t->init_block->thread_counter));
+    }
+  }
+  
+  async event void ThreadNotification.aboutToDestroy[uint8_t id]()
+  {
+    thread_t *t = call ThreadScheduler.threadInfo(id);
+    if(t->init_block != NULL) {
+      call ReferenceCounter.decrement(&(t->init_block->thread_counter));
+    }
+  }
+
+  task void loadDoneTask()
+  {
+    if (retError != SUCCESS)
+      handler = TOSTHREAD_INVALID_THREAD_ID;
+
+    if (readSource == READSOURCE_MEMORY) {
+      signal DynamicLoader.loadFromMemoryDone(((void *)readSourceOffset), handler, retError);
+    } else {
+      signal DynamicLoader.loadFromFlashDone(readSource, handler, retError);
+    }
+  }
+    
+  void initProgDesc()
+  {
+    prog_desc.main_addr = 0;
+    prog_desc.alloc_count = 0;
+    prog_desc.g_reloc_count = 0;
+    prog_desc.l_reloc_count = 0;
+    prog_desc.code_count = 0;
+    prog_desc.patch_table_count = 0;
+
+    prog_desc.loading_stage = 0;
+    codePtr = 0;
+  }
+
+  void errorHandler()
+  {
+    call Leds.set(7);
+    
+    if (tablesMemory != NULL) { free(tablesMemory); tablesMemory = NULL; }
+    if (gVarMemory != NULL) { free(gVarMemory); gVarMemory = NULL; }
+    if (proc != NULL) { free(proc); proc = NULL; }
+    initProgDesc();
+    
+    retError = FAIL;
+    post loadDoneTask();
+  }
+
+  void run_proc(void *arg)
+  {
+    init_block_t* curProc = arg;
+    thread_t* t = call ThreadScheduler.currentThreadInfo();
+    t->init_block = curProc;
+    
+    (*(curProc->init_ptr))(curProc->init_arg);
+    call ReferenceCounter.waitOnValue(&(curProc->thread_counter), 0);
+    
+    call PMManager.release(codeFirstAddrs[t->id], threadCodeSizes[t->id]);
+    codeFirstAddrs[t->id] = 0;
+    threadCodeSizes[t->id] = 0;
+    if (curProc->globals != NULL) {
+      free(curProc->globals);
+    }
+    free(curProc);
+  }
+
+  task void start_prog()
+  {
+    free(tablesMemory);
+    proc = malloc(sizeof(init_block_t));
+    proc->globals = gVarMemory;
+    proc->init_ptr = (void *)((uint16_t)code + prog_desc.main_addr);
+    proc->init_arg = NULL;
+    call ReferenceCounter.init( &(proc->thread_counter) );
+    
+    if (call DynamicThread.create(&handler, run_proc, proc, 200) == SUCCESS) {  
+      codeFirstAddrs[handler] = (uint16_t)code;
+      threadCodeSizes[handler] = prog_desc.code_count;
+      retError = SUCCESS;
+      post loadDoneTask();
+    } else {
+      retError = FAIL;
+      post loadDoneTask();
+      if (proc->globals != NULL) {
+        free(proc->globals);
+      }
+      free(proc);
+    }
+    
+    initProgDesc();
+  }
+
+  // Gets write access to the internal flash
+  void eeprom_w()
+  {
+    FCTL2 = FWKEY + FSSEL1 + FN2;   // selects SMCLK and divides it by 4
+    FCTL3 = FWKEY;   // enables the writing/erasing by clearing the LOCK bit
+    FCTL1 = FWKEY + WRT;   // enables the writing
+  }
+
+  // Gets read-only access to the internal flash
+  void eeprom_r()
+  {
+    FCTL1 = FWKEY;   // Clear WRT bit
+    FCTL3 = FWKEY + LOCK;   // disabling the writing/erasing
+  }
+
+  // Calculates where should the code be placed in the internal flash
+  /*
+  uint16_t eeprom_code_addr()
+  {
+    uint16_t addr;
+    
+    addr = (((prog_desc.code_count - 1) / 512) + 1) * 512;   // Spaces needed for the code
+    addr = 0xFFFF - 512 - addr + 1;   // The 1 is to align the code
+    
+    return addr; 
+  }
+  */
+
+  // Loads program image meta data
+  void loader_metaData()
+  {
+    prog_desc.patch_table_count = prog_desc.alloc_count +
+                                  prog_desc.g_reloc_count +
+                                  prog_desc.l_reloc_count;
+    prog_desc.code_offset = sizeof(prog_desc.main_addr) +
+                            sizeof(prog_desc.alloc_count) +
+                            sizeof(prog_desc.alloc_size) +
+                            sizeof(prog_desc.g_reloc_count) +
+                            sizeof(prog_desc.l_reloc_count) +
+                            sizeof(prog_desc.datasec_count) +
+                            sizeof(prog_desc.code_count) +
+                            (prog_desc.patch_table_count * 4) +
+                            (prog_desc.datasec_count * 6);
+    
+    if (prog_desc.patch_table_count > 0 || prog_desc.datasec_count > 0) {
+      if ((prog_desc.patch_table_count * 4) > (prog_desc.datasec_count * 6)) {
+        tablesMemory = malloc(prog_desc.patch_table_count * 4);
+      } else {
+        tablesMemory = malloc(prog_desc.datasec_count * 6);
+      }
+    } else {
+      tablesMemory = NULL;
+    }
+  }
+
+  // Prepares the patch table before patching addresses in the binary code
+  void loader_patch_table()
+  {
+    uint16_t i, tempUInt16 = 0;
+    
+    // Find out the total space global variables need, and malloc
+    /*
+    for (i = 0; i < (prog_desc.alloc_count * 4); i+=4) {
+      tempUInt16 += *((uint16_t *)&tablesMemory[i]);
+      *((uint16_t *)&tablesMemory[i]) = tempUInt16 - *((uint16_t *)&tablesMemory[i]);
+    }
+    */
+    if (prog_desc.alloc_size > 0) {
+      gVarMemory = malloc(prog_desc.alloc_size);
+      memset(gVarMemory, 0, prog_desc.alloc_size);
+    } else {
+      gVarMemory = NULL;
+    }
+    
+    // Some "real" addresses need offsets added. For example, local relocation table entries need
+    // the starting code address
+    for (i = 0; i < (prog_desc.patch_table_count * 4); i+=4) {
+      if (i < (prog_desc.alloc_count * 4)) {
+        tempUInt16 = (uint16_t)gVarMemory;   // Allocation table needs memory's offset
+      } else if (i < ((prog_desc.alloc_count + prog_desc.g_reloc_count) * 4)) {
+        tempUInt16 = 0;   // Global relocation table doesn't need anything
+      } else {
+        tempUInt16 = (uint16_t)code;   // Local relocation table needs code's offset
+      }
+      *((uint16_t *)&(tablesMemory[i])) = *((uint16_t *)&(tablesMemory[i])) + tempUInt16;   // Writes the real address
+    }
+    
+    // Converts function IDs in global relocation table to real addresses
+    for (i = (prog_desc.alloc_count * 4); i < ((prog_desc.alloc_count + prog_desc.g_reloc_count) * 4); i+=4) {
+      tempUInt16 = *((uint16_t *)&tablesMemory[i]);   // Gets function ID
+      tempUInt16 = (uint16_t)fun[tempUInt16].addr;   // Gets the real address of the function ID
+      *((uint16_t *)&tablesMemory[i]) = tempUInt16;   // Writes the real address
+    }
+  }
+  
+  void loader_addr_1()
+  {
+    uint16_t i, laddr = 0, raddr = 0;
+    
+    // Resets before start
+    nextTask_chain = 0x0;
+    nextAddr = 0;
+    
+    // Gets the next task by searching for the lowest next patching address
+    raddr = 0xFFFF;   // Temp variable to store the minimum patching address so far
+    laddr = 0;   // Temp variable to store the current patching address
+    for (i = 0; i < (prog_desc.patch_table_count * 4); i+=4) {
+      laddr = *((uint16_t *)&tablesMemory[i + 2]);
+      
+      if (((uint16_t)nextTask_chain == 0x0 && laddr != 0xFFFF) || 
+           raddr > laddr) {
+        nextTask_chain = &(tablesMemory[i]);
+        raddr = laddr;
+      }
+    }
+    
+    if (nextTask_chain != 0x0) {
+      // Gets the next patching address in the chain from the flash
+      raddr = *((uint16_t *)&nextTask_chain[2]);
+      call ImageRead.read[readSource](readSourceOffset + prog_desc.code_offset + raddr, 
+                                      &nextAddr, 
+                                      2);
+    } else {
+      // Copies the rest of the binary code
+      call ImageRead.read[readSource](readSourceOffset + prog_desc.code_offset + codePtr, 
+                                      &(code[codePtr]), 
+                                      prog_desc.code_count - codePtr);
+      prog_desc.loading_stage++;
+    }
+  }
+  
+  // Patches the part of binary code that needs "real" addresses
+  void loader_addr_2()
+  {
+    uint16_t laddr;
+    
+    laddr = *((uint16_t *)&nextTask_chain[2]);   // Gets the current patching address
+    
+    // Updates the chain with the next patching address
+    if (nextAddr == 0x0000) {
+      nextAddr = 0xFFFF;   // End of chain, marks it with a big number
+    }
+    *((uint16_t *)&nextTask_chain[2]) = nextAddr;
+    
+    // Patches address in the binary code
+    *((uint16_t *)&code[laddr]) = *((uint16_t *)&nextTask_chain[0]);
+    
+    // Copies the binary code between the last patching address and the current patching address
+    call ImageRead.read[readSource](readSourceOffset + prog_desc.code_offset + codePtr, 
+                                    &(code[codePtr]), 
+                                    laddr - codePtr);
+    codePtr = laddr + 2;   // Notes up to what location in the binary code we have copied
+  }
+  
+  void loader_datasec()
+  {
+    uint16_t i, j;
+    
+    for (i = 0; i < (prog_desc.datasec_count * 6); i+=6) {
+      uint16_t destAddr = *((uint16_t *)&(tablesMemory[i])) + (uint16_t)gVarMemory;   // Writes the real address
+      uint16_t srcAddr = *((uint16_t *)&(tablesMemory[i + 2])) + (uint16_t)code;   // Writes the real address
+      uint16_t size = *((uint16_t *)&(tablesMemory[i + 4]));
+      
+      for (j = 0; j < size; j++) {
+        ((uint8_t *)((void *)(destAddr + j)))[0] = ((uint8_t *)((void *)(srcAddr + j)))[0];
+        //*((uint8_t *)&code[destAddr + j]) = *((uint8_t *)&code[srcAddr + j]);
+      }
+    }
+  }
+  
+  void loadProgram()
+  {
+    error_t error = SUCCESS;
+    switch (prog_desc.loading_stage) {
+      case 0:
+        // Loads meta data to memory array
+        error = call ImageRead.read[readSource](readSourceOffset + 0, 
+                                                &prog_desc, 
+                                                7 * 2);
+        if (error == SUCCESS) {
+          prog_desc.loading_stage++;   // Moves to next loading phase
+        } else {
+          errorHandler();
+        }
+        break;
+      
+      case 1:
+        loader_metaData();   // Gets meta data
+        code = (void *) call PMManager.request(prog_desc.code_count);   // Gets the location of where the code will be copied to
+        
+        if ((uint16_t)code != 0xFFFF) {
+          // Loads patch table to memory array
+          error = call ImageRead.read[readSource](readSourceOffset + 7 * 2, 
+                                                  tablesMemory, 
+                                                  prog_desc.patch_table_count * 4);
+          if (error == SUCCESS) {
+            prog_desc.loading_stage++;   // Moves to next loading phase
+          } else {
+            errorHandler();
+          }
+        } else {
+          errorHandler();
+        }
+        
+        break;
+      
+      case 2:
+        loader_patch_table();
+        
+        eeprom_w();   // Gets write-access to internal flash
+        prog_desc.loading_stage++;   // Moves to next loading phase
+      
+      case 3:
+        loader_addr_1();
+        break;
+      
+      case 4:
+        eeprom_r();   // Locks the internal flash back
+        
+        error = call ImageRead.read[readSource](readSourceOffset + 7 * 2 + prog_desc.patch_table_count * 4, 
+                                                tablesMemory, 
+                                                prog_desc.datasec_count * 6);
+        if (error == SUCCESS) {
+          prog_desc.loading_stage++;   // Moves to next loading phase
+        } else {
+          errorHandler();
+        }
+        
+        break;
+      case 5:
+        loader_datasec();
+        prog_desc.loading_stage++;
+        
+      case 6:
+        post start_prog();
+        break;
+    }
+  }
+  
+  command error_t Init.init()
+  {
+    int i;
+    for (i = 0; i < TOSTHREAD_MAX_NUM_THREADS; i++) {
+      threadCodeSizes[i] = 0;
+      codeFirstAddrs[i] = 0;
+    }
+    initProgDesc();
+    return SUCCESS;
+  }
+  
+  task void taskLoadProgram()
+  {
+    loadProgram();
+  }
+  
+  error_t start_load(uint8_t in_readSource, uint16_t in_readSourceOffset)
+  {
+    if (prog_desc.loading_stage == 0) {
+      uint16_t i;
+      call Leds.set(7);
+      for (i = 0; i < 2000; i++) { }
+      call Leds.set(0);
+      
+      readSource = in_readSource;
+      readSourceOffset = in_readSourceOffset;
+      post taskLoadProgram();   // Start Loading
+      
+      return SUCCESS;
+    }
+    
+    return EBUSY;
+  }
+  
+  command error_t DynamicLoader.loadFromFlash(uint8_t volumeId) { return start_load(volumeId, 0); }
+  
+  command error_t DynamicLoader.loadFromMemory(void *addr) { return start_load(READSOURCE_MEMORY, (uint16_t)addr); }
+  
+  event void ImageRead.readDone[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len, error_t error)
+  {
+    if (error == SUCCESS) {
+      if (buf == &nextAddr) {
+        loader_addr_2();
+      } else {
+        post taskLoadProgram();
+      }
+    } else {
+      errorHandler();
+    }
+  }
+
+#ifndef DISABLE_LOADER_USERBUTTON
+  event void UserButton.fired()
+  {
+     call DynamicLoader.loadFromFlash(VOLUME_MICROEXEIMAGE);
+  }
+#endif
+  
+  event void ImageRead.computeCrcDone[uint8_t id](storage_addr_t addr, storage_len_t len, uint16_t crc, error_t error) {}
+  
+  default command error_t ImageRead.read[uint8_t id](storage_addr_t addr, void *buf, storage_len_t len) { return FAIL; }
+  default event void DynamicLoader.loadFromFlashDone(uint8_t volumeId, tosthread_t id, error_t error) {}
+  default event void DynamicLoader.loadFromMemoryDone(void *addr, tosthread_t id, error_t error) {}
+}