Update tadpole to include the latest larva code.
diff --git a/.gitignore b/.gitignore
index b146445..2f35cd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,10 @@
+.repo.db
+bin
+obj
+tags
+.gdb_history
+*~
 .nest
+.DS_Store
+*.swp
+*.swo
diff --git a/hw/hal/include/hal/hal_flash.h b/hw/hal/include/hal/hal_flash.h
index be3b358..787e9c2 100644
--- a/hw/hal/include/hal/hal_flash.h
+++ b/hw/hal/include/hal/hal_flash.h
@@ -25,6 +25,7 @@
   uint32_t num_bytes);
 int hal_flash_erase_sector(uint8_t flash_id, uint32_t sector_address);
 int hal_flash_erase(uint8_t flash_id, uint32_t address, uint32_t num_bytes);
+uint8_t hal_flash_align(uint8_t flash_id);
 int hal_flash_init(void);
 
 #endif
diff --git a/hw/hal/include/hal/hal_flash_int.h b/hw/hal/include/hal/hal_flash_int.h
index ab7e159..34cd2c4 100644
--- a/hw/hal/include/hal/hal_flash_int.h
+++ b/hw/hal/include/hal/hal_flash_int.h
@@ -34,6 +34,7 @@
     uint32_t hf_base_addr;
     uint32_t hf_size;
     int hf_sector_cnt;
+    int hf_align;		/* Alignment requirement. 1 if unrestricted. */
 };
 
 /*
diff --git a/hw/hal/include/hal/hal_spim.h b/hw/hal/include/hal/hal_spim.h
deleted file mode 100644
index fa1eeeb..0000000
--- a/hw/hal/include/hal/hal_spim.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) 2015 Runtime Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef H_HAL_SPIM_H_
-#define H_HAL_SPIM_H_
-
-#include <inttypes.h>
-
-/**
- *
- * hal_spim_init()
- *
- * Sets up a SPI master port ready for data transfer.
- * Mapping logical port number to pins is dictated by BSP.
- */
-int hal_spim_init(int port, int mode, int speed);
-
-/*
- * hal_spim_select()/hal_spim_deselect.
- * Assert and deassert chip select for the target, respectively.
- */
-int hal_spim_select(int port);
-int hal_spim_deselect(int port);
-
-/**
- *
- * hal_spim_txrx()
- * Initiate data transfer. Specify TX data or RX data, or both.
- */
-int hal_spim_txrx(int port, void *tx_buf, int tx_len, void *rx_buf, int rx_len);
-
-#endif /* H_HAL_SPIM_H_ */
diff --git a/hw/hal/include/hal/hal_system.h b/hw/hal/include/hal/hal_system.h
index 19f5ed1..1cf43c4 100644
--- a/hw/hal/include/hal/hal_system.h
+++ b/hw/hal/include/hal/hal_system.h
@@ -4,7 +4,7 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
@@ -17,6 +17,14 @@
 #ifndef H_HAL_SYSTEM_
 #define H_HAL_SYSTEM_
 
+/*
+ * System reset.
+ */
 void system_reset(void) __attribute((noreturn));
 
+/*
+ * Called by bootloader to start loaded program.
+ */
+void system_start(void *img_start) __attribute((noreturn));
+
 #endif
diff --git a/hw/hal/src/hal_flash.c b/hw/hal/src/hal_flash.c
index 90fd985..ef4e35a 100644
--- a/hw/hal/src/hal_flash.c
+++ b/hw/hal/src/hal_flash.c
@@ -39,6 +39,18 @@
     return rc;
 }
 
+uint8_t
+hal_flash_align(uint8_t flash_id)
+{
+    const struct hal_flash *hf;
+
+    hf = bsp_flash_dev(flash_id);
+    if (!hf) {
+        return 1;
+    }
+    return hf->hf_align;
+}
+
 uint32_t
 hal_flash_sector_size(const struct hal_flash *hf, int sec_idx)
 {
diff --git a/hw/mcu/native/src/hal_flash.c b/hw/mcu/native/src/hal_flash.c
index 98eb6df..5fc3e6c 100644
--- a/hw/mcu/native/src/hal_flash.c
+++ b/hw/mcu/native/src/hal_flash.c
@@ -65,6 +65,7 @@
     .hf_base_addr = 0,
     .hf_size = 1024 * 1024,
     .hf_sector_cnt = FLASH_NUM_AREAS,
+    .hf_align = 1
 };
 
 static void
@@ -85,7 +86,7 @@
     }
     file = open(name, O_RDWR);
     if (file < 0) {
-        file = open(name, O_RDWR | O_CREAT);
+        file = open(name, O_RDWR | O_CREAT, 0660);
         assert(file > 0);
         created = 1;
         if (ftruncate(file, native_flash_dev.hf_size) < 0) {
@@ -155,6 +156,7 @@
 static int
 native_flash_write(uint32_t address, const void *src, uint32_t length)
 {
+    assert(address % native_flash_dev.hf_align == 0);
     return flash_native_write_internal(address, src, length, 0);
 }
 
diff --git a/libs/os/include/os/endian.h b/libs/os/include/os/endian.h
new file mode 100644
index 0000000..c8c838a
--- /dev/null
+++ b/libs/os/include/os/endian.h
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_ENDIAN_
+#define H_ENDIAN_
+
+#include <inttypes.h>
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#ifndef ntohll
+#define ntohll(x)  ((uint64_t)(x))
+#endif
+
+#ifndef htonll
+#define htonll(x)  ((uint64_t)(x))
+#endif
+
+#ifndef ntohl
+#define ntohl(x)   ((uint32_t)(x))
+#endif
+
+#ifndef htonl
+#define htonl(x)   ((uint32_t)(x))
+#endif
+
+#ifndef ntohs
+#define ntohs(x)   ((uint16_t)(x))
+#endif
+
+#ifndef htons
+#define htons(x)   ((uint16_t)(x))
+#endif
+
+#else
+
+#ifndef ntohll
+#define ntohll(x)   ((uint64_t)                     \
+     ((((x) & 0xff00000000000000ull) >> 56) |       \
+      (((x) & 0x00ff000000000000ull) >> 40) |       \
+      (((x) & 0x0000ff0000000000ull) >> 24) |       \
+      (((x) & 0x000000ff00000000ull) >>  8) |       \
+      (((x) & 0x00000000ff000000ull) <<  8) |       \
+      (((x) & 0x0000000000ff0000ull) << 24) |       \
+      (((x) & 0x000000000000ff00ull) << 40) |       \
+      (((x) & 0x00000000000000ffull) << 56)))
+#endif
+
+#ifndef htonll
+#define htonll      ntohll
+#endif
+
+#ifndef ntohl
+#define ntohl(x)    ((uint32_t)                     \
+    ((((x) & 0xff000000) >> 24) |                   \
+     (((x) & 0x00ff0000) >>  8) |                   \
+     (((x) & 0x0000ff00) <<  8) |                   \
+     (((x) & 0x000000ff) << 24)))
+#endif
+
+#ifndef htonl
+#define htonl       ntohl
+#endif
+
+#ifndef htons
+#define htons(x)   ((uint16_t)                      \
+    ((((x) & 0xff00) >> 8) |                        \
+     (((x) & 0x00ff) << 8)))
+#endif
+
+#ifndef ntohs
+#define ntohs       htons
+#endif
+
+#endif
+#endif
diff --git a/libs/os/include/os/os_mbuf.h b/libs/os/include/os/os_mbuf.h
index 881ddba..9eaee38 100644
--- a/libs/os/include/os/os_mbuf.h
+++ b/libs/os/include/os/os_mbuf.h
@@ -17,6 +17,7 @@
 #ifndef _OS_MBUF_H 
 #define _OS_MBUF_H 
 
+#include "os/queue.h"
 #include "os/os_eventq.h"
 
 /**
@@ -40,6 +41,11 @@
      * The memory pool which to allocate mbufs out of 
      */
     struct os_mempool *omp_pool;
+
+    /**
+     * Link to the next mbuf pool for system memory pools.
+     */
+    STAILQ_ENTRY(os_mbuf_pool) omp_next;
 };
 
 
@@ -126,6 +132,12 @@
 #define OS_MBUF_PKTHDR_TO_MBUF(__hdr)   \
      (struct os_mbuf *)((uint8_t *)(__hdr) - sizeof(struct os_mbuf))
 
+/**
+ * Gets the length of an entire mbuf chain.  The specified mbuf must have a
+ * packet header.
+ */
+#define OS_MBUF_PKTLEN(__om) (OS_MBUF_PKTHDR(__om)->omp_len)
+
 /*
  * Access the data of a mbuf, and cast it to type
  *
@@ -201,6 +213,16 @@
 /* Put an element in a mbuf queue */
 int os_mqueue_put(struct os_mqueue *, struct os_eventq *, struct os_mbuf *);
 
+/* Register an mbuf pool with the system pool registry */
+int os_msys_register(struct os_mbuf_pool *);
+
+/* Return a mbuf from the system pool, given an indicative mbuf size */
+struct os_mbuf *os_msys_get(uint16_t dsize, uint16_t leadingspace);
+
+/* Return a packet header mbuf from the system pool */
+struct os_mbuf *os_msys_get_pkthdr(uint16_t dsize, uint16_t pkthdr_len);
+
+
 /* Initialize a mbuf pool */
 int os_mbuf_pool_init(struct os_mbuf_pool *, struct os_mempool *mp, 
         uint16_t, uint16_t);
@@ -237,5 +259,6 @@
 int os_mbuf_copyinto(struct os_mbuf *om, int off, const void *src, int len);
 void os_mbuf_concat(struct os_mbuf *first, struct os_mbuf *second);
 void *os_mbuf_extend(struct os_mbuf *om, uint16_t len);
+struct os_mbuf *os_mbuf_pullup(struct os_mbuf *om, uint16_t len);
 
 #endif /* _OS_MBUF_H */ 
diff --git a/libs/os/include/os/os_mutex.h b/libs/os/include/os/os_mutex.h
index 716ba60..a16c370 100644
--- a/libs/os/include/os/os_mutex.h
+++ b/libs/os/include/os/os_mutex.h
@@ -22,11 +22,11 @@
 
 struct os_mutex
 {
+    SLIST_HEAD(, os_task) mu_head;  /* chain of waiting tasks */
     uint8_t     _pad;
     uint8_t     mu_prio;            /* owner's default priority*/
     uint16_t    mu_level;           /* call nesting level */
     struct os_task *mu_owner;       /* owners task */
-    SLIST_HEAD(, os_task) mu_head;  /* chain of waiting tasks */
 };
 
 /* 
diff --git a/libs/os/include/os/os_sem.h b/libs/os/include/os/os_sem.h
index 73e84ee..b0eb8bf 100644
--- a/libs/os/include/os/os_sem.h
+++ b/libs/os/include/os/os_sem.h
@@ -21,9 +21,9 @@
 
 struct os_sem
 {
+    SLIST_HEAD(, os_task) sem_head;     /* chain of waiting tasks */
     uint16_t    _pad;
     uint16_t    sem_tokens;             /* # of tokens */
-    SLIST_HEAD(, os_task) sem_head; /* chain of waiting tasks */
 };
 
 /* 
diff --git a/libs/os/include/os/os_task.h b/libs/os/include/os/os_task.h
index 3f75432..ae44a73 100644
--- a/libs/os/include/os/os_task.h
+++ b/libs/os/include/os/os_task.h
@@ -29,6 +29,18 @@
 #define OS_TASK_NAME_SIZE (36) 
 #endif 
 
+/* 
+ * Generic "object" structure. All objects that a task can wait on must
+ * have a SLIST_HEAD(, os_task) head_name as the first element in the object 
+ * structure. The element 'head_name' can be any name. See os_mutex.h or
+ * os_sem.h for an example.
+ */
+struct os_task_obj
+{
+    SLIST_HEAD(, os_task) obj_head;     /* chain of waiting tasks */
+};
+
+/* Task states */
 typedef enum os_task_state {
     OS_TASK_READY = 1, 
     OS_TASK_SLEEP = 2
@@ -37,6 +49,7 @@
 /* Task flags */
 #define OS_TASK_FLAG_NO_TIMEOUT     (0x0001U)
 #define OS_TASK_FLAG_SEM_WAIT       (0x0002U)
+#define OS_TASK_FLAG_MUTEX_WAIT     (0x0004U)
 
 typedef void (*os_task_func_t)(void *);
 
@@ -54,7 +67,7 @@
     os_task_func_t t_func;
     void *t_arg;
 
-    struct os_mutex *t_mutex;
+    void *t_obj;
 
     struct os_sanity_check t_sanity_check; 
 
diff --git a/libs/os/include/os/queue.h b/libs/os/include/os/queue.h
index 39826dd..4f5cacf 100755
--- a/libs/os/include/os/queue.h
+++ b/libs/os/include/os/queue.h
@@ -242,7 +242,7 @@
 	(STAILQ_EMPTY(head) ?						\
 		NULL :							\
 	        ((struct type *)					\
-		((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+		((char *)((head)->stqh_last) - offsetof(struct type, field))))
 
 #define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
 
diff --git a/libs/os/src/arch/sim/os_fault.c b/libs/os/src/arch/sim/os_fault.c
index 8bd8fbe..f6aa6cb 100644
--- a/libs/os/src/arch/sim/os_fault.c
+++ b/libs/os/src/arch/sim/os_fault.c
@@ -27,4 +27,5 @@
 
     snprintf(msg, sizeof(msg), "assert at %s:%d\n", file, line);
     write(1, msg, strlen(msg));
+    _exit(1);
 }
diff --git a/libs/os/src/os_eventq.c b/libs/os/src/os_eventq.c
index 3515a22..ca729db 100644
--- a/libs/os/src/os_eventq.c
+++ b/libs/os/src/os_eventq.c
@@ -48,6 +48,7 @@
     resched = 0;
     if (evq->evq_task) {
         os_sched_wakeup(evq->evq_task);
+        evq->evq_task = NULL;
         resched = 1;
     }
 
diff --git a/libs/os/src/os_info.c b/libs/os/src/os_info.c
index ebb6f12..2387cdd 100644
--- a/libs/os/src/os_info.c
+++ b/libs/os/src/os_info.c
@@ -69,7 +69,7 @@
     name = NULL;
     found = 0;
 
-    if (argv[1] != NULL && strcmp(argv[1], "")) {
+    if (argc > 1 && strcmp(argv[1], "")) {
         name = argv[1];
     }   
 
@@ -95,7 +95,7 @@
         }
 
         console_printf("  %s (prio: %u, nw: %u, flags: 0x%x, "
-                "ssize: %u, cswcnt: %lu, tot_run_time: %ums)", 
+                "ssize: %u, cswcnt: %lu, tot_run_time: %ums)\n", 
                 info[i].oti_name, 
                 info[i].oti_prio, info[i].oti_next_wakeup, info[i].oti_flags, 
                 info[i].oti_stack_size,
diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c
index b1be0fc..f14466a 100644
--- a/libs/os/src/os_mbuf.c
+++ b/libs/os/src/os_mbuf.c
@@ -54,6 +54,9 @@
 
 #include <string.h>
 
+STAILQ_HEAD(, os_mbuf_pool) g_msys_pool_list = 
+    STAILQ_HEAD_INITIALIZER(g_msys_pool_list);
+
 int 
 os_mqueue_init(struct os_mqueue *mq, void *arg)
 {
@@ -84,7 +87,11 @@
     }
     OS_EXIT_CRITICAL(sr);
 
-    m = OS_MBUF_PKTHDR_TO_MBUF(mp);
+    if (mp) {
+        m = OS_MBUF_PKTHDR_TO_MBUF(mp);
+    } else {
+        m = NULL;
+    }
 
     return (m);
 }
@@ -117,6 +124,83 @@
     return (rc);
 }
 
+int 
+os_msys_register(struct os_mbuf_pool *new_pool)  
+{
+    struct os_mbuf_pool *pool;
+
+    pool = NULL;
+    STAILQ_FOREACH(pool, &g_msys_pool_list, omp_next) {
+        if (new_pool->omp_databuf_len > pool->omp_databuf_len) {
+            break;
+        }
+    }
+
+    if (pool) {
+        STAILQ_INSERT_AFTER(&g_msys_pool_list, pool, new_pool, omp_next);
+    } else {
+        STAILQ_INSERT_TAIL(&g_msys_pool_list, new_pool, omp_next);
+    }
+
+    return (0);
+}
+
+static struct os_mbuf_pool *
+_os_msys_find_pool(uint16_t dsize) 
+{
+    struct os_mbuf_pool *pool;
+
+    pool = NULL;
+    STAILQ_FOREACH(pool, &g_msys_pool_list, omp_next) {
+        if (dsize <= pool->omp_databuf_len) {
+            break;
+        }
+    }
+
+    if (!pool) {
+        pool = STAILQ_LAST(&g_msys_pool_list, os_mbuf_pool, omp_next);
+    }
+
+    return (pool);
+}
+
+
+
+struct os_mbuf *
+os_msys_get(uint16_t dsize, uint16_t leadingspace)
+{
+    struct os_mbuf *m;
+    struct os_mbuf_pool *pool;
+
+    pool = _os_msys_find_pool(dsize);
+    if (!pool) {
+        goto err;
+    }
+
+    m = os_mbuf_get(pool, leadingspace);
+    return (m);
+err:
+    return (NULL);
+}
+
+
+struct os_mbuf *
+os_msys_get_pkthdr(uint16_t dsize, uint16_t pkthdr_len)
+{
+    struct os_mbuf *m;
+    struct os_mbuf_pool *pool;
+
+    pool = _os_msys_find_pool(dsize + pkthdr_len);
+    if (!pool) {
+        goto err;
+    }
+    
+    m = os_mbuf_get_pkthdr(pool, pkthdr_len);
+    return (m);
+err:
+    return (NULL);
+}
+
 
 /**
  * Initialize a pool of mbufs. 
@@ -161,6 +245,7 @@
 
     SLIST_NEXT(om, om_next) = NULL;
     om->om_flags = 0;
+    om->om_pkthdr_len = 0;
     om->om_len = 0;
     om->om_data = (&om->om_databuf[0] + leadingspace);
     om->om_omp = omp;
@@ -204,9 +289,11 @@
 {
     int rc;
 
-    rc = os_memblock_put(om->om_omp->omp_pool, om);
-    if (rc != 0) {
-        goto err;
+    if (om->om_omp != NULL) {
+        rc = os_memblock_put(om->om_omp->omp_pool, om);
+        if (rc != 0) {
+            goto err;
+        }
     }
 
     return (0);
@@ -829,8 +916,6 @@
     return data;
 }
 
-#if 0
-
 /**
  * Rearrange a mbuf chain so that len bytes are contiguous, 
  * and in the data area of an mbuf (so that OS_MBUF_DATA() will 
@@ -851,40 +936,58 @@
 os_mbuf_pullup(struct os_mbuf *om, uint16_t len)
 {
     struct os_mbuf_pool *omp;
-    struct os_mbuf *newm;
+    struct os_mbuf *next;
+    struct os_mbuf *om2;
+    int count;
+    int space;
 
     omp = om->om_omp;
 
-    if (len > omp->omp_databuf_len) {
-        goto err;
-    }
-
-    /* Is 'n' bytes already contiguous? */
-    if (((uint8_t *) &om->om_databuf[0] + omp->omp_databuf_len) - 
-            OS_MBUF_DATA(om, uint8_t *) >= len) {
-        newm = om;
-        goto done;
-    }
-
-    /* Nope, OK. Allocate a new buffer, and then go through and copy 'n' 
-     * bytes into that buffer.
+    /*
+     * If first mbuf has no cluster, and has room for len bytes
+     * without shifting current data, pullup into it,
+     * otherwise allocate a new mbuf to prepend to the chain.
      */
-    newm = os_mbuf_get(omp, 0);
-    if (!newm) {
-        goto err;
+    if (om->om_len >= len) {
+        return (om);
     }
-
-    written = 0; 
-    while (written < len
-
-
-done:
-    return (newm);
-err:
-    if (om) {
-        os_mbuf_free_chain(om);
+    if (om->om_len + OS_MBUF_TRAILINGSPACE(om) >= len &&
+        SLIST_NEXT(om, om_next)) {
+        om2 = om;
+        om = SLIST_NEXT(om, om_next);
+        len -= om2->om_len;
+    } else {
+        if (len > omp->omp_databuf_len - om->om_pkthdr_len)
+            goto bad;
+        om2 = os_mbuf_get(omp, 0);
+        if (om2 == NULL)
+            goto bad;
+        if (OS_MBUF_IS_PKTHDR(om))
+            _os_mbuf_copypkthdr(om2, om);
     }
-
+    space = OS_MBUF_TRAILINGSPACE(om2);
+    do {
+        count = min(min(len, space), om->om_len);
+        memcpy(om2->om_data + om2->om_len, om->om_data, count);
+        len -= count;
+        om2->om_len += count;
+        om->om_len -= count;
+        space -= count;
+        if (om->om_len)
+            om->om_data += count;
+        else {
+            next = SLIST_NEXT(om, om_next);
+            os_mbuf_free(om);
+            om = next;
+        }
+    } while (len > 0 && om);
+    if (len > 0) {
+        os_mbuf_free(om2);
+        goto bad;
+    }
+    SLIST_NEXT(om2, om_next) = om;
+    return (om2);
+bad:
+    os_mbuf_free_chain(om);
     return (NULL);
 }
-#endif
diff --git a/libs/os/src/os_mutex.c b/libs/os/src/os_mutex.c
index 3ad3151..0965be8 100644
--- a/libs/os/src/os_mutex.c
+++ b/libs/os/src/os_mutex.c
@@ -98,11 +98,7 @@
     rdy = SLIST_FIRST(&mu->mu_head);
     if (rdy) {
         /* There is one waiting. Wake it up */
-        assert(rdy->t_mutex);
-        rdy->t_mutex = NULL;
-
-        SLIST_REMOVE_HEAD(&mu->mu_head, t_obj_list);
-        SLIST_NEXT(rdy, t_obj_list) = NULL;
+        assert(rdy->t_obj);
         os_sched_wakeup(rdy);
 
         /* Set mutex internals */
@@ -214,13 +210,17 @@
     }
 
     /* Set mutex pointer in task */
-    current->t_mutex = mu;
+    current->t_obj = mu;
+    current->t_flags |= OS_TASK_FLAG_MUTEX_WAIT;
     os_sched_sleep(current, timeout);
-
     OS_EXIT_CRITICAL(sr);
 
     os_sched(NULL, 0);
 
+    OS_ENTER_CRITICAL(sr);
+    current->t_flags &= ~OS_TASK_FLAG_MUTEX_WAIT;
+    OS_EXIT_CRITICAL(sr);
+
     /* If we are owner we did not time out. */
     if (mu->mu_owner == current) {
         rc = OS_OK; 
@@ -276,10 +276,7 @@
     /* Now, go through all the tasks waiting on the mutex */
     while (!SLIST_EMPTY(&mu->mu_head)) {
         rdy = SLIST_FIRST(&mu->mu_head);
-        assert(rdy->t_mutex);
-        rdy->t_mutex = NULL;
-        SLIST_REMOVE_HEAD(&mu->mu_head, t_obj_list);
-        SLIST_NEXT(rdy, t_obj_list) = NULL;
+        assert(rdy->t_obj);
         os_sched_wakeup(rdy);
     }
 
diff --git a/libs/os/src/os_sched.c b/libs/os/src/os_sched.c
index 944f0f6..f1c03c6 100644
--- a/libs/os/src/os_sched.c
+++ b/libs/os/src/os_sched.c
@@ -243,12 +243,17 @@
 int 
 os_sched_wakeup(struct os_task *t) 
 {
-    /* Remove self from mutex list if waiting on one */
-    if (t->t_mutex) {
-        assert(!SLIST_EMPTY(&t->t_mutex->mu_head));
-        SLIST_REMOVE(&t->t_mutex->mu_head, t, os_task, t_obj_list);
+    struct os_task_obj *os_obj;
+
+    assert(t->t_state == OS_TASK_SLEEP);
+
+    /* Remove self from object list if waiting on one */
+    if (t->t_obj) {
+        os_obj = (struct os_task_obj *)t->t_obj;
+        assert(!SLIST_EMPTY(&os_obj->obj_head));
+        SLIST_REMOVE(&os_obj->obj_head, t, os_task, t_obj_list);
         SLIST_NEXT(t, t_obj_list) = NULL;
-        t->t_mutex = NULL; 
+        t->t_obj = NULL; 
     }
 
     /* Remove task from sleep list */
diff --git a/libs/os/src/os_sem.c b/libs/os/src/os_sem.c
index d3b395c..84464d3 100644
--- a/libs/os/src/os_sem.c
+++ b/libs/os/src/os_sem.c
@@ -86,12 +86,8 @@
     /* Check if tasks are waiting for the semaphore */
     rdy = SLIST_FIRST(&sem->sem_head);
     if (rdy) {
-        /* Clear flag that we are waiting on the semaphore */
+        /* Clear flag that we are waiting on the semaphore; wake up task */
         rdy->t_flags &= ~OS_TASK_FLAG_SEM_WAIT;
-
-        /* There is one waiting. Wake it up */
-        SLIST_REMOVE_HEAD(&sem->sem_head, t_obj_list);
-        SLIST_NEXT(rdy, t_obj_list) = NULL;
         os_sched_wakeup(rdy);
 
         /* Schedule if waiting task higher priority */
@@ -169,6 +165,7 @@
         rc = OS_OK;
 
         /* Link current task to tasks waiting for semaphore */
+        current->t_obj = sem; 
         current->t_flags |= OS_TASK_FLAG_SEM_WAIT;
         last = NULL;
         if (!SLIST_EMPTY(&sem->sem_head)) {
@@ -258,9 +255,7 @@
 
         /* Now, go through all the tasks waiting on the semaphore */
         while (rdy != NULL) {
-            SLIST_REMOVE_HEAD(&sem->sem_head, t_obj_list);
-            SLIST_NEXT(rdy, t_obj_list) = NULL;
-            os_sched_wakeup(rdy);
+            os_sched_wakeup(rdy); 
             rdy = SLIST_FIRST(&sem->sem_head);
         }
     }
diff --git a/libs/os/src/test/mbuf_test.c b/libs/os/src/test/mbuf_test.c
index 8652494..fb7c4de 100644
--- a/libs/os/src/test/mbuf_test.c
+++ b/libs/os/src/test/mbuf_test.c
@@ -50,6 +50,8 @@
     struct os_mbuf *m; 
     int rc;
 
+    os_mbuf_test_setup();
+
     m = os_mbuf_get(&os_mbuf_pool, 0);
     TEST_ASSERT_FATAL(m != NULL, "Error allocating mbuf");
 
@@ -64,6 +66,8 @@
     struct os_mbuf *dup;
     int rc;
 
+    os_mbuf_test_setup();
+
     /* Test first allocating and duplicating a single mbuf */
     m = os_mbuf_get(&os_mbuf_pool, 0);
     TEST_ASSERT_FATAL(m != NULL, "Error allocating mbuf");
@@ -106,6 +110,8 @@
     uint8_t databuf[] = {0xa, 0xb, 0xc, 0xd};
     uint8_t cmpbuf[] = {0xff, 0xff, 0xff, 0xff};
 
+    os_mbuf_test_setup();
+
     m = os_mbuf_get(&os_mbuf_pool, 0);
     TEST_ASSERT_FATAL(m != NULL, "Error allocating mbuf");
 
@@ -121,15 +127,98 @@
             "Databuf doesn't match cmpbuf");
 }
 
-TEST_SUITE(os_mbuf_test_case_4)
+static void
+os_mbuf_test_misc_assert_contiguous(struct os_mbuf *om, void *data, int len)
 {
+    TEST_ASSERT_FATAL(om != NULL);
+
+    if (OS_MBUF_IS_PKTHDR(om)) {
+        TEST_ASSERT(OS_MBUF_PKTLEN(om) == len);
+    }
+    TEST_ASSERT(om->om_len == len);
+    TEST_ASSERT(memcmp(om->om_data, data, len) == 0);
+}
+
+TEST_CASE(os_mbuf_test_pullup)
+{
+    struct os_mbuf *om;
+    struct os_mbuf *om2;
+    uint8_t data[256];
+    int rc;
+    int i;
+
+    os_mbuf_test_setup();
+
+    for (i = 0; i < sizeof data; i++) {
+        data[i] = i;
+    }
+
+    /*** Free when too much data is requested. */
+    om = os_mbuf_get_pkthdr(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om != NULL);
+
+    om = os_mbuf_pullup(om, 1);
+    TEST_ASSERT(om == NULL);
+
+    /*** No effect when all data is already at the start. */
+    om = os_mbuf_get_pkthdr(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om != NULL);
+
+    rc = os_mbuf_append(om, data, 1);
+    TEST_ASSERT_FATAL(rc == 0);
+    os_mbuf_test_misc_assert_contiguous(om, data, 1);
+
+    om = os_mbuf_pullup(om, 1);
+    os_mbuf_test_misc_assert_contiguous(om, data, 1);
+
+    /*** Spread data across four mbufs. */
+    om2 = os_mbuf_get(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om2 != NULL);
+    rc = os_mbuf_append(om2, data + 1, 1);
+    TEST_ASSERT_FATAL(rc == 0);
+    os_mbuf_concat(om, om2);
+
+    om2 = os_mbuf_get(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om2 != NULL);
+    rc = os_mbuf_append(om2, data + 2, 1);
+    TEST_ASSERT_FATAL(rc == 0);
+    os_mbuf_concat(om, om2);
+
+    om2 = os_mbuf_get(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om2 != NULL);
+    rc = os_mbuf_append(om2, data + 3, 1);
+    TEST_ASSERT_FATAL(rc == 0);
+    os_mbuf_concat(om, om2);
+
+    TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(om) == 4);
+
+    om = os_mbuf_pullup(om, 4);
+    os_mbuf_test_misc_assert_contiguous(om, data, 4);
+
+    os_mbuf_free_chain(om);
+
+    /*** Require an allocation. */
+    om = os_mbuf_get_pkthdr(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om != NULL);
+
+    om->om_data += 100;
+    rc = os_mbuf_append(om, data, 100);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    om2 = os_mbuf_get(&os_mbuf_pool, 0);
+    TEST_ASSERT_FATAL(om2 != NULL);
+    rc = os_mbuf_append(om2, data + 100, 100);
+    TEST_ASSERT_FATAL(rc == 0);
+    os_mbuf_concat(om, om2);
+
+    om = os_mbuf_pullup(om, 200);
+    os_mbuf_test_misc_assert_contiguous(om, data, 200);
 }
 
 TEST_SUITE(os_mbuf_test_suite)
 {
-    os_mbuf_test_setup();
-
     os_mbuf_test_case_1();
     os_mbuf_test_case_2();
     os_mbuf_test_case_3();
+    os_mbuf_test_pullup();
 }
diff --git a/libs/os/src/test/mutex_test.c b/libs/os/src/test/mutex_test.c
index a861dbb..7d9a08c 100644
--- a/libs/os/src/test/mutex_test.c
+++ b/libs/os/src/test/mutex_test.c
@@ -178,6 +178,14 @@
         g_task14_val = 1;
         os_time_delay(100);
 
+        /* 
+         * Task17 should have its mutex wait flag set; at least the first time
+         * through!
+         */
+        if (iters == 0) {
+            TEST_ASSERT(task17.t_flags & OS_TASK_FLAG_MUTEX_WAIT);
+        }
+
         if (g_mutex_test == 4) {
             os_mutex_delete(&g_mutex1);
             os_time_delay(150);
@@ -303,6 +311,7 @@
             err = os_mutex_pend(&g_mutex1, 10);
         } else {
             err = os_mutex_pend(&g_mutex1, 10000);
+            TEST_ASSERT((t->t_flags & OS_TASK_FLAG_MUTEX_WAIT) == 0);
         }
 
         if (g_mutex_test == 4 || g_mutex_test == 5) {
diff --git a/libs/os/src/test/sem_test.c b/libs/os/src/test/sem_test.c
index 75b2e40..fbaf82b 100644
--- a/libs/os/src/test/sem_test.c
+++ b/libs/os/src/test/sem_test.c
@@ -95,7 +95,7 @@
 
     while (1) {
         err = os_sem_pend(&g_sem1, timeout);
-        TEST_ASSERT(err == OS_OK);
+        TEST_ASSERT((err == OS_OK) || (err == OS_TIMEOUT));
 
         err = os_sem_release(&g_sem1);
         TEST_ASSERT(err == OS_OK);
diff --git a/libs/util/include/util/base64.h b/libs/util/include/util/base64.h
new file mode 100644
index 0000000..fff40fd
--- /dev/null
+++ b/libs/util/include/util/base64.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __UTIL_BASE64_H 
+#define __UTIL_BASE64_H 
+
+#include <stdint.h>
+
+int base64_encode(const void *, int, char *, uint8_t);
+int base64_decode(const char *, void *buf);
+
+#define BASE64_ENCODE_SIZE(__size) (((__size) * (4 / 3)) + 4)
+
+#endif /* __UTIL_BASE64_H__ */
diff --git a/libs/util/src/base64.c b/libs/util/src/base64.c
new file mode 100644
index 0000000..3b07362
--- /dev/null
+++ b/libs/util/src/base64.c
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 
+ * This file is based on roken from the FreeBSD source.  It has been modified
+ * to not use malloc() and instead expect static buffers, and tabs have been 
+ * replaced with spaces.  Also, instead of strlen() on the resulting string, 
+ * pointer arithmitic is done, as p represents the end of the buffer.
+ */
+
+/* 
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <util/base64.h>
+
+static const char base64_chars[] = 
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int 
+pos(char c)
+{
+    const char *p;
+    for (p = base64_chars; *p; p++)
+        if (*p == c)
+            return p - base64_chars;
+    return -1;
+}
+
+int 
+base64_encode(const void *data, int size, char *s, uint8_t should_pad)
+{
+    char *p;
+    int i;
+    int c;
+    const unsigned char *q;
+    char *last;
+    int diff;
+
+    p = s;
+
+    q = (const unsigned char *) data;
+    last = NULL;
+    i = 0;
+    while (i < size) {
+        c = q[i++];
+        c *= 256;
+        if (i < size)
+            c += q[i];
+        i++;
+        c *= 256;
+        if (i < size)
+            c += q[i];
+        i++;
+        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+        p[1] = base64_chars[(c & 0x0003f000) >> 12];
+        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+        p[3] = base64_chars[(c & 0x0000003f) >> 0];
+        last = p;
+        p += 4;
+    }
+
+    if (last) {
+        diff = i - size;
+        if (diff > 0) {
+            if (should_pad) {
+                memset(last + (4 - diff), '=', diff);
+            } else {
+                p = last + (4 - diff);
+            }
+        }
+    } 
+
+    *p = 0;
+
+    return (p - s);
+}
+
+#define DECODE_ERROR -1
+
+static unsigned int
+token_decode(const char *token)
+{
+    int i;
+    unsigned int val = 0;
+    int marker = 0;
+    if (strlen(token) < 4)
+        return DECODE_ERROR;
+    for (i = 0; i < 4; i++) {
+        val *= 64;
+        if (token[i] == '=')
+            marker++;
+        else if (marker > 0)
+            return DECODE_ERROR;
+        else
+            val += pos(token[i]);
+    }
+    if (marker > 2)
+        return DECODE_ERROR;
+    return (marker << 24) | val;
+}
+
+int 
+base64_decode(const char *str, void *data)
+{
+    const char *p;
+    unsigned char *q;
+
+    q = data;
+    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+        unsigned int val = token_decode(p);
+        unsigned int marker = (val >> 24) & 0xff;
+        if (val == DECODE_ERROR)
+            return -1;
+        *q++ = (val >> 16) & 0xff;
+        if (marker < 2)
+            *q++ = (val >> 8) & 0xff;
+        if (marker < 1)
+            *q++ = val & 0xff;
+    }
+    return q - (unsigned char *) data;
+}
diff --git a/libs/util/src/flash_map.c b/libs/util/src/flash_map.c
index 28f26c9..81d0f22 100644
--- a/libs/util/src/flash_map.c
+++ b/libs/util/src/flash_map.c
@@ -82,36 +82,69 @@
 }
 
 #ifdef NFFS_PRESENT
+/*
+ * Turn flash region into a set of areas for NFFS use.
+ *
+ * Limit the number of regions we return to be less than *cnt.
+ * If sector count within region exceeds that, collect multiple sectors
+ * to a region.
+ */
 int
 flash_area_to_nffs_desc(int idx, int *cnt, struct nffs_area_desc *nad)
 {
-    int i;
+    int i, j;
     const struct hal_flash *hf;
     const struct flash_area *fa;
+    int max_cnt, move_on;
+    int first_idx, last_idx;
     uint32_t start, size;
+    uint32_t min_size;
 
     if (!flash_map || idx >= flash_map_entries) {
         return -1;
     }
+    first_idx = last_idx = -1;
+    max_cnt = *cnt;
     *cnt = 0;
+
     fa = &flash_map[idx];
 
     hf = bsp_flash_dev(fa->fa_flash_id);
     for (i = 0; i < hf->hf_sector_cnt; i++) {
         hf->hf_itf->hff_sector_info(i, &start, &size);
         if (start >= fa->fa_off && start < fa->fa_off + fa->fa_size) {
-            if (nad) {
-                nad->nad_flash_id = fa->fa_flash_id;
-                nad->nad_offset = start;
-                nad->nad_length = size;
-                nad++;
+            if (first_idx == -1) {
+                first_idx = i;
             }
+            last_idx = i;
             *cnt = *cnt + 1;
         }
     }
-    if (nad) {
-        memset(nad, 0, sizeof(*nad));
+    if (*cnt > max_cnt) {
+        min_size = fa->fa_size / max_cnt;
+    } else {
+        min_size = 0;
     }
+    *cnt = 0;
+
+    move_on = 1;
+    for (i = first_idx, j = 0; i < last_idx + 1; i++) {
+        hf->hf_itf->hff_sector_info(i, &start, &size);
+        if (move_on) {
+            nad[j].nad_flash_id = fa->fa_flash_id;
+            nad[j].nad_offset = start;
+            nad[j].nad_length = size;
+            *cnt = *cnt + 1;
+            move_on = 0;
+        } else {
+            nad[j].nad_length += size;
+        }
+        if (nad[j].nad_length >= min_size) {
+            j++;
+            move_on = 1;
+        }
+    }
+    nad[*cnt].nad_length = 0;
     return 0;
 }
 #endif /* NFFS_PRESENT */
diff --git a/libs/util/src/stats.c b/libs/util/src/stats.c
index 0d2eb36..e61d785 100644
--- a/libs/util/src/stats.c
+++ b/libs/util/src/stats.c
@@ -43,6 +43,7 @@
 struct shell_cmd shell_stats_cmd;
 #endif
 
+
 #ifdef SHELL_PRESENT 
 
 static void 
@@ -130,6 +131,7 @@
 
 #endif
 
+
 int 
 stats_module_init(void)
 {
@@ -141,6 +143,7 @@
     }
 #endif
 
+
     rc = stats_init(STATS_HDR(stats), STATS_SIZE_INIT_PARMS(stats, STATS_SIZE_32), 
             STATS_NAME_INIT_PARMS(stats));
     if (rc != 0) {