Extend lc_open() to also being able to load overlay plugins.

For this it needs to look at the plugin header. Since lc_open() doesn't know
it's a plugin, the header needs to be changed slightly to include the new lc_header (which needs to be the first element in plugin_header so it can be casted savely).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28054 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/codecs.c b/apps/codecs.c
index 35f6363..3fa05be 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -181,7 +181,8 @@
 
 static int codec_load_ram(void *handle, struct codec_api *api)
 {
-    struct codec_header *hdr = lc_get_header(handle);
+    struct codec_header *c_hdr = lc_get_header(handle);
+    struct lc_header    *hdr   = c_hdr ? &c_hdr->lc_hdr : NULL;
     int status;
 
     if (hdr == NULL
@@ -215,8 +216,8 @@
     codec_size = 0;
 #endif
 
-    *(hdr->api) = api;
-    status = hdr->entry_point();
+    *(c_hdr->api) = api;
+    status = c_hdr->entry_point();
 
     lc_close(handle);
 
diff --git a/apps/codecs.h b/apps/codecs.h
index f94c81a..1c0b9da 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -51,6 +51,7 @@
 #include "settings.h"
 
 #include "gcc_extensions.h"
+#include "load_code.h"
 
 #ifdef CODEC
 #if defined(DEBUG) || defined(SIMULATOR)
@@ -240,11 +241,7 @@
 
 /* codec header */
 struct codec_header {
-    unsigned long magic; /* RCOD or RENC */
-    unsigned short target_id;
-    unsigned short api_version;
-    unsigned char *load_addr;
-    unsigned char *end_addr;
+    struct lc_header lc_hdr; /* must be first */
     enum codec_status(*entry_point)(void);
     struct codec_api **api;
 };
@@ -261,27 +258,27 @@
 #define CODEC_HEADER \
         const struct codec_header __header \
         __attribute__ ((section (".header")))= { \
-        CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
-        plugin_start_addr, plugin_end_addr, codec_start, &ci };
+        { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
+        plugin_start_addr, plugin_end_addr }, codec_start, &ci };
 /* encoders */
 #define CODEC_ENC_HEADER \
         const struct codec_header __header \
         __attribute__ ((section (".header")))= { \
-        CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
-        plugin_start_addr, plugin_end_addr, codec_start, &ci };
+        { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
+        plugin_start_addr, plugin_end_addr }, codec_start, &ci };
 
 #else /* def SIMULATOR */
 /* decoders */
 #define CODEC_HEADER \
         const struct codec_header __header \
         __attribute__((visibility("default"))) = { \
-        CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
-        NULL, NULL, codec_start, &ci };
+        { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
+        codec_start, &ci };
 /* encoders */
 #define CODEC_ENC_HEADER \
         const struct codec_header __header = { \
-        CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
-        NULL, NULL, codec_start, &ci };
+        { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
+        codec_start, &ci };
 #endif /* SIMULATOR */
 #endif /* CODEC */
 
diff --git a/apps/codecs/codec_crt0.c b/apps/codecs/codec_crt0.c
index fdb7909..c845f79 100644
--- a/apps/codecs/codec_crt0.c
+++ b/apps/codecs/codec_crt0.c
@@ -20,15 +20,10 @@
  ****************************************************************************/
 
 #include "config.h"
-#include "codeclib.h"
+#include "codecs.h"
 
 struct codec_api *ci DATA_ATTR;
 
-extern unsigned char iramcopy[];
-extern unsigned char iramstart[];
-extern unsigned char iramend[];
-extern unsigned char iedata[];
-extern unsigned char iend[];
 extern unsigned char plugin_bss_start[];
 extern unsigned char plugin_end_addr[];
 
@@ -42,14 +37,23 @@
 {
 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
 #ifdef USE_IRAM
-    ci->memcpy(iramstart, iramcopy, iramend - iramstart);
-    ci->memset(iedata, 0, iend - iedata);
-#endif
+    extern char iramcopy[], iramstart[], iramend[], iedata[], iend[];
+    size_t iram_size = iramend - iramstart;
+    size_t ibss_size = iend - iedata;
+    if (iram_size > 0 || ibss_size > 0)
+    {
+        ci->memcpy(iramstart, iramcopy, iram_size);
+        ci->memset(iedata, 0, ibss_size);
+        /* make the icache (if it exists) up to date with the new code */
+        ci->cpucache_invalidate();
+        /* barrier to prevent reordering iram copy and BSS clearing,
+         * because the BSS segment alias the IRAM copy.
+         */
+        asm volatile ("" ::: "memory");
+    }
+#endif /* PLUGIN_USE_IRAM */
     ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start);
 #endif
-    /* writeback cleared iedata and bss areas, invalidate icache for
-     * copied code */
-    ci->cpucache_invalidate();
 
     return codec_main();
 }
diff --git a/apps/plugin.c b/apps/plugin.c
index 9e08951..9b490d0 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -723,12 +723,18 @@
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
     dir_get_info,
+
+    lc_open,
+    lc_open_from_mem,
+    lc_get_header,
+    lc_close,
 };
 
 int plugin_load(const char* plugin, const void* parameter)
 {
     int rc, i;
-    struct plugin_header *hdr;
+    struct plugin_header *p_hdr;
+    struct lc_header     *hdr;
 
 #if LCD_DEPTH > 1
     fb_data* old_backdrop;
@@ -754,7 +760,10 @@
         return -1;
     }
 
-    hdr = lc_get_header(current_plugin_handle);
+    p_hdr = lc_get_header(current_plugin_handle);
+
+    hdr = p_hdr ? &p_hdr->lc_hdr : NULL;
+    
 
     if (hdr == NULL
         || hdr->magic != PLUGIN_MAGIC
@@ -782,7 +791,7 @@
     plugin_size = 0;
 #endif
 
-    *(hdr->api) = &rockbox_api;
+    *(p_hdr->api) = &rockbox_api;
 
 #if defined HAVE_LCD_BITMAP && LCD_DEPTH > 1
     old_backdrop = lcd_get_backdrop();
@@ -806,7 +815,7 @@
     open_files = 0;
 #endif
 
-    rc = hdr->entry_point(parameter);
+    rc = p_hdr->entry_point(parameter);
 
     if (!pfn_tsr_exit)
     {   /* close handle if plugin is no tsr one */
diff --git a/apps/plugin.h b/apps/plugin.h
index bafd407..a69b85b 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -53,6 +53,7 @@
 #include "thread.h"
 #include "button.h"
 #include "action.h"
+#include "load_code.h"
 #include "usb.h"
 #include "font.h"
 #include "lcd.h"
@@ -895,15 +896,17 @@
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
     struct dirinfo (*dir_get_info)(DIR* parent, struct dirent *entry);
+
+    /* load code api for overlay */
+    void* (*lc_open)(const char *filename, unsigned char *buf, size_t buf_size);
+    void* (*lc_open_from_mem)(void* addr, size_t blob_size);
+    void* (*lc_get_header)(void *handle);
+    void  (*lc_close)(void *handle);
 };
 
 /* plugin header */
 struct plugin_header {
-    unsigned long magic;
-    unsigned short target_id;
-    unsigned short api_version;
-    unsigned char *load_addr;
-    unsigned char *end_addr;
+    struct lc_header lc_hdr; /* must be the first */
     enum plugin_status(*entry_point)(const void*);
     const struct plugin_api **api;
 };
@@ -916,15 +919,15 @@
         const struct plugin_api *rb DATA_ATTR; \
         const struct plugin_header __header \
         __attribute__ ((section (".header")))= { \
-        PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, \
-        plugin_start_addr, plugin_end_addr, plugin__start, &rb };
+        { PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, \
+        plugin_start_addr, plugin_end_addr }, plugin__start, &rb };
 #else /* PLATFORM_HOSTED */
 #define PLUGIN_HEADER \
         const struct plugin_api *rb DATA_ATTR; \
         const struct plugin_header __header \
         __attribute__((visibility("default"))) = { \
-        PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, \
-        NULL, NULL, plugin__start, &rb };
+        { PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, NULL, NULL },
+        plugin__start, &rb };
 #endif /* CONFIG_PLATFORM */
 #endif /* PLUGIN */
 
diff --git a/apps/plugins/lib/overlay.c b/apps/plugins/lib/overlay.c
index 8a04cf8..f64df29 100644
--- a/apps/plugins/lib/overlay.c
+++ b/apps/plugins/lib/overlay.c
@@ -47,64 +47,56 @@
 enum plugin_status run_overlay(const void* parameter,
                                unsigned char *filename, unsigned char *name)
 {
-    int fd, readsize;
     size_t audiobuf_size;
     unsigned char *audiobuf;
-    static struct plugin_header header;
-
-    fd = rb->open(filename, O_RDONLY);
-    if (fd < 0)
-    {
-        rb->splashf(2*HZ, "Can't open %s", filename);
-        return PLUGIN_ERROR;
-    }
-    readsize = rb->read(fd, &header, sizeof(header));
-    rb->close(fd);
-    /* Close for now. Less code than doing it in all error checks.
-     * Would need to seek back anyway. */
-
-    if (readsize != sizeof(header))
-    {
-        rb->splashf(2*HZ, "Reading %s overlay failed.", name);
-        return PLUGIN_ERROR;
-    }
-    if (header.magic != PLUGIN_MAGIC || header.target_id != TARGET_ID)
-    {
-        rb->splashf(2*HZ, "%s overlay: Incompatible model.", name);
-        return PLUGIN_ERROR;
-    }
-    if (header.api_version != PLUGIN_API_VERSION) 
-    {
-        rb->splashf(2*HZ, "%s overlay: Incompatible version.", name);
-        return PLUGIN_ERROR;
-    }
+    void *handle;
+    struct plugin_header *p_hdr;
+    struct lc_header     *hdr;
 
     audiobuf = rb->plugin_get_audio_buffer(&audiobuf_size);
-    if (header.load_addr < audiobuf ||
-        header.end_addr > audiobuf + audiobuf_size)
+    if (!audiobuf)
     {
-        rb->splashf(2*HZ, "%s overlay doesn't fit into memory.", name);
-        return PLUGIN_ERROR;
+        rb->splash(2*HZ, "Can't optain memory");
+        goto error;
     }
 
-    fd = rb->open(filename, O_RDONLY);
-    if (fd < 0)
+    handle = rb->lc_open(filename, audiobuf, audiobuf_size);
+    if (!handle)
     {
         rb->splashf(2*HZ, "Can't open %s", filename);
-        return PLUGIN_ERROR;
+        goto error;
     }
-    readsize = rb->read(fd, header.load_addr, header.end_addr - header.load_addr);
-    rb->close(fd);
 
-    if (readsize < 0)
+    p_hdr = rb->lc_get_header(handle);
+    if (!p_hdr)
     {
-        rb->splashf(2*HZ, "Reading %s overlay failed.", name);
-        return PLUGIN_ERROR;
+        rb->splash(2*HZ, "Can't get header");
+        goto error_close;
     }
-    /* Zero out bss area */
-    rb->memset(header.load_addr + readsize, 0,
-               header.end_addr - (header.load_addr + readsize));
+    else
+        hdr = &p_hdr->lc_hdr;
 
-    *(header.api) = rb;
-    return header.entry_point(parameter);
+    if (hdr->magic != PLUGIN_MAGIC || hdr->target_id != TARGET_ID)
+    {
+        rb->splashf(2*HZ, "%s overlay: Incompatible model.", name);
+        goto error_close;
+    }
+
+    
+    if (hdr->api_version > PLUGIN_API_VERSION
+        || hdr->api_version < PLUGIN_MIN_API_VERSION)
+    {
+        rb->splashf(2*HZ, "%s overlay: Incompatible version.", name);
+        goto error_close;
+    }
+
+    rb->lc_close(handle);
+
+    *(p_hdr->api) = rb;
+    return p_hdr->entry_point(parameter);
+
+error_close:
+    rb->lc_close(handle);
+error:
+    return PLUGIN_ERROR;
 }
diff --git a/firmware/export/load_code.h b/firmware/export/load_code.h
index 6f5e5d0..55ce601 100644
--- a/firmware/export/load_code.h
+++ b/firmware/export/load_code.h
@@ -20,17 +20,21 @@
  ****************************************************************************/
 
 
+#ifndef __LOAD_CODE_H__
+#define __LOAD_CODE_H__
+
 #include "config.h"
 
 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
 #include "system.h"
 
-extern void *lc_open(const char *filename, char *buf, size_t buf_size);
+extern void *lc_open(const char *filename, unsigned char *buf, size_t buf_size);
 /* header is always at the beginning of the blob, and handle actually points
- * to the start of the blob */
+ * to the start of the blob (the header is there) */
 static inline void *lc_open_from_mem(void* addr, size_t blob_size)
 {
     (void)blob_size;
+    /* commit dcache and discard icache */
     cpucache_invalidate();
     return addr;
 }
@@ -48,14 +52,27 @@
 #else
 #define _lc_open_char char
 #endif
-extern void *_lc_open(const _lc_open_char *filename, char *buf, size_t buf_size);
+extern void *_lc_open(const _lc_open_char *filename, unsigned char *buf, size_t buf_size);
 extern void *_lc_get_header(void *handle);
 extern void  _lc_close(void *handle);
 
-extern void *lc_open(const char *filename, char *buf, size_t buf_size);
-/* not possiible on hosted platforms */
+extern void *lc_open(const char *filename, unsigned char *buf, size_t buf_size);
 extern void *lc_open_from_mem(void *addr, size_t blob_size);
 extern void *lc_get_header(void *handle);
 extern void  lc_close(void *handle);
 extern const char* lc_last_error(void);
 #endif
+
+/* this struct needs to be the first part of other headers
+ * (lc_open() casts the other header to this one to load to the correct
+ * address)
+ */
+struct lc_header {
+    unsigned long magic;
+    unsigned short target_id;
+    unsigned short api_version;
+    unsigned char *load_addr;
+    unsigned char *end_addr;
+};
+
+#endif /* __LOAD_CODE_H__ */
diff --git a/firmware/load_code.c b/firmware/load_code.c
index 75bac8b..5b5307e 100644
--- a/firmware/load_code.c
+++ b/firmware/load_code.c
@@ -26,37 +26,77 @@
 #include "load_code.h"
 
 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+
 /* load binary blob from disk to memory, returning a handle */
-void * lc_open(const char *filename, char *buf, size_t buf_size)
+void * lc_open(const char *filename, unsigned char *buf, size_t buf_size)
 {
     int fd = open(filename, O_RDONLY);
     ssize_t read_size;
+    struct lc_header hdr;
+    unsigned char *buf_end = buf+buf_size;
+    off_t copy_size;
 
     if (fd < 0)
     {
         DEBUGF("Could not open file");
-        return NULL;
+        goto error;
     }
 
 #if NUM_CORES > 1
     /* Make sure COP cache is flushed and invalidated before loading */
     {
         int my_core = switch_core(CURRENT_CORE ^ 1);
-        cpucache_invalidate();
+        cpucache_commit_discard();
         switch_core(my_core);
     }
 #endif
 
-    read_size = read(fd, buf, buf_size);
-    close(fd);
-    cpucache_invalidate();
+    /* read the header to obtain the load address */
+    read_size = read(fd, &hdr, sizeof(hdr));
 
     if (read_size < 0)
     {
         DEBUGF("Could not read from file");
-        return NULL;
+        goto error_fd;
     }
-    return buf;
+
+    /* hdr.end_addr points to the end of the bss section,
+     * but there might be idata/icode behind that so the bytes to copy
+     * can be larger */
+    copy_size = MAX(filesize(fd), hdr.end_addr - hdr.load_addr);
+
+    if (hdr.load_addr < buf || (hdr.load_addr+copy_size) > buf_end)
+    {
+        DEBUGF("Binary doesn't fit into memory");
+        goto error_fd;
+    }
+
+    /* go back to beginning to load the whole thing (incl. header) */
+    if (lseek(fd, 0, SEEK_SET) < 0)
+    {
+        DEBUGF("lseek failed");
+        goto error_fd;
+    }
+
+    /* the header has the addresses where the code is linked at */
+    read_size = read(fd, hdr.load_addr, copy_size);
+    close(fd);
+
+    if (read_size < 0)
+    {
+        DEBUGF("Could not read from file");
+        goto error;
+    }
+
+    /* commit dcache and discard icache */
+    cpucache_commit_discard();
+    /* return a pointer the header, reused by lc_get_header() */
+    return hdr.load_addr;
+
+error_fd:
+    close(fd);
+error:
+    return NULL;
 }
 
 #elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
@@ -85,12 +125,11 @@
 #include "rbpaths.h"
 #include "general.h"
 
-void * _lc_open(const _lc_open_char *filename, char *buf, size_t buf_size)
+void * _lc_open(const _lc_open_char *filename, unsigned char *buf, size_t buf_size)
 {
     (void)buf;
     (void)buf_size;
-    void* dl_handle = dlopen(filename, RTLD_NOW);
-    return dl_handle;
+    return dlopen(filename, RTLD_NOW);
 }
 
 void *lc_open_from_mem(void *addr, size_t blob_size)
diff --git a/firmware/target/hosted/android/lc-android.c b/firmware/target/hosted/android/lc-android.c
index 52ab08b..434e901 100644
--- a/firmware/target/hosted/android/lc-android.c
+++ b/firmware/target/hosted/android/lc-android.c
@@ -23,7 +23,7 @@
 #include "load_code.h"
 
 /* the load_code wrappers simply wrap, nothing to do */
-void *lc_open(const char *filename, char *buf, size_t buf_size)
+void *lc_open(const char *filename, unsigned char *buf, size_t buf_size)
 {
     return _lc_open(filename, buf, buf_size);
 }
diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c
index 56f1339..3591905 100644
--- a/uisimulator/common/io.c
+++ b/uisimulator/common/io.c
@@ -526,7 +526,7 @@
 
 #ifndef __PCTOOL__
 
-void *lc_open(const char *filename, char *buf, size_t buf_size)
+void *lc_open(const char *filename, unsigned char *buf, size_t buf_size)
 {
     const char *sim_path = get_sim_pathname(filename);
     void *handle = _lc_open(UTF8_TO_OS(sim_path), buf, buf_size);