/**************************************************************************** * apps/system/install/install.c * * Copyright (C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise * * 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 NuttX 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 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 OWNER 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define ACTION_INSTALL 0x01 #define ACTION_REMOVE 0x00 #define ACTION_REINSTALL 0x03 #define ACTION_INSUFPARAM 0x80 #define INSTALL_PROGRAMBLOCKSIZE 1024 /**************************************************************************** * Private data ****************************************************************************/ static const char *install_help = "Installs XIP program into flash and creates a start-up script in the\n" "destination directory.\n\n" "Usage:\t%s [options] source-file.xip destination-directory\n\n" "Example:\n\t%s --stack 1024 /sdcard/demo.xip /usr/bin\n\n" "Options:\n" "\t--stack \n" "\t--priority \n" "\t--remove \tRemoves installed application\n" "\t--force\t\t\tReplaces existing installation\n" "\t--start \t\tInstalls application at or after \n" "\t--margin \tLeave some free space after the kernel (default 16)\n"; static const char *install_script_text = "# XIP stacksize=%x priority=%x size=%x\n"; static const char *install_script_exec = "exec 0x%x\n"; /**************************************************************************** * Private functions ****************************************************************************/ static int install_getstartpage(int startpage, int pagemargin, int desiredsize) { size_t page = 0, stpage = 0xffff; size_t pagesize = 0; int maxlen = -1; int maxlen_start = 0xffff; ssize_t status; for (status=0, page=0; status >= 0; page++) { status = up_progmem_ispageerased(page); pagesize = up_progmem_pagesize(page); /* Is this beginning of new free space section */ if (status == 0) { if (stpage == 0xffff) stpage = page; } else if (status != 0) { if (stpage != 0xffff) { if ((page - stpage) > maxlen) { if (maxlen==-1) { /* First time found sth? */ stpage += pagemargin; maxlen = 0; } if (stpage < startpage) { stpage = startpage; } if (page > stpage) { maxlen = page - stpage; maxlen_start = stpage; } if (maxlen*pagesize >= desiredsize) { /* printf("Found page at %d ... %d\n", stpage, page); */ return maxlen_start*pagesize; } } stpage = 0xffff; } } } /* Requested space is not available */ return -1; } static int install_programflash(int startaddr, const char *source) { ssize_t status; size_t count; ssize_t totalsize = 0; char *buf; FILE *fp; if ((buf = malloc(INSTALL_PROGRAMBLOCKSIZE)) == NULL) { return -ENOMEM; } if ((fp = fopen(source, "r"))) { do { count = fread(buf, 1, INSTALL_PROGRAMBLOCKSIZE, fp); if ((status = up_progmem_write(startaddr, buf, count)) < 0) { totalsize = status; break; } startaddr += count; totalsize += count; } while (count); } else { totalsize = -errno; } fclose(fp); free(buf); return totalsize; } static void install_getscriptname(char *scriptname, const char *progname, const char *destdir) { const char * progonly; /* I.e. as /usr/bin */ strcpy(scriptname, destdir); /* extract from i.e. /sdcard/demo -> /demo, together with / */ progonly = strrchr(progname, '/'); strcat(scriptname, progonly); } static int install_getprogsize(const char *progname) { struct stat fileinfo; if (stat(progname, &fileinfo) < 0) { return -1; } return fileinfo.st_size; } static int install_alreadyexists(const char *scriptname) { FILE *fp; if ((fp = fopen(scriptname, "r")) == NULL) { return 0; } fclose(fp); return 1; } static int install_createscript(int addr, int stacksize, int progsize, int priority, const char *scriptname) { FILE *fp; if ((fp = fopen(scriptname, "w+")) == NULL) { return -errno; } fprintf(fp, install_script_text, stacksize, priority, progsize); fprintf(fp, install_script_exec, addr); fflush(fp); fclose(fp); return 0; } static int install_getlasthexvalue(FILE *fp, char delimiter) { char buf[128]; char *p; if (fgets(buf, 127, fp)) { if ((p = strrchr(buf, delimiter))) { return strtol(p+1, NULL, 16); } } return -1; } static int install_remove(const char *scriptname) { FILE *fp; int progsize, addr, freedsize; ssize_t page; int status = 0; /* Parse script */ if ((fp = fopen(scriptname, "r"))) { progsize = install_getlasthexvalue(fp,'='); addr = install_getlasthexvalue(fp,' '); freedsize = progsize; } else { return -errno; } fclose(fp); /* Remove pages */ if (progsize <= 0 || addr <= 0) { return -EIO; } do { if ((page = up_progmem_getpage(addr)) < 0) { status = -page; break; } if (up_progmem_erasepage(page) < 0) { status = -page; break; } addr += up_progmem_pagesize(page); progsize -= up_progmem_pagesize(page); } while (progsize > 0); if (status < 0) { return status; } /* Remove script file */ if (unlink(scriptname) < 0) { return -errno; } return freedsize; } /**************************************************************************** * Public Functions ****************************************************************************/ #ifdef CONFIG_BUILD_KERNEL int main(int argc, FAR char *argv[]) #else int install_main(int argc, char *argv[]) #endif { int i; int progsize; int scrsta; int stacksize = 4096; int priority = SCHED_PRIORITY_DEFAULT; int pagemargin = 16; int startpage = 0; int startaddr = 0; int action = ACTION_INSTALL; char scriptname[128]; /* Supported? */ if (!up_progmem_isuniform()) { fprintf(stderr, "Error: install supports uniform organization only.\n"); return -1; } /* Parse arguments */ for (i = 1; i < argc; i++) { if (i <= argc && argv[i][0]=='-' && argv[i][1]=='-') { if (strcmp(argv[i]+2, "stack")==0) { stacksize = atoi(argv[++i]); } else if (strcmp(argv[i]+2, "priority")==0) { priority = atoi(argv[++i]); } else if (strcmp(argv[i]+2, "start")==0) { startpage = atoi(argv[++i]); } else if (strcmp(argv[i]+2, "margin")==0) { pagemargin = atoi(argv[++i]); } else if (strcmp(argv[i]+2, "remove")==0) { action = ACTION_REMOVE; } else if (strcmp(argv[i]+2, "force")==0) { action = ACTION_REINSTALL; } else fprintf(stderr, "Unknown option: %s\n", argv[i]); } else { break; } } /* Do the job */ switch(action & 1) { case ACTION_REMOVE: if (i > argc-1) { action = ACTION_INSUFPARAM; break; /* are there sufficient parameters */ } if ((scrsta=install_remove(argv[i])) < 0) { fprintf(stderr, "Could not remove program: %s\n", strerror(-scrsta)); return -1; } printf("Removed %s and freed %d bytes\n", argv[i], scrsta); return 0; case ACTION_INSTALL: if (i > argc-2) { action = ACTION_INSUFPARAM; break; /* are there sufficient parameters */ } install_getscriptname(scriptname, argv[i], argv[i+1]); /* script-exists? */ if (install_alreadyexists(scriptname) == 1) { if (action != ACTION_REINSTALL) { fprintf(stderr, "Program with that name already exists.\n"); return -EEXIST; } if ((scrsta = install_remove(scriptname)) < 0) { fprintf(stderr, "Could not remove program: %s\n", strerror(-scrsta)); return -1; } printf("Replacing %s\n", scriptname); } startaddr = install_getstartpage(startpage, pagemargin, install_getprogsize(argv[i])); if (startpage < 0) { fprintf(stderr, "Not enough memory\n"); return -ENOMEM; } if ((progsize = install_programflash(startaddr, argv[i])) <= 0) { fprintf(stderr, "Error writing program memory: %s\n" "Note: Flash pages are not released, so you may try again and program will be\n" " written in other pages.\n", strerror(-progsize)); return -EIO; } if ((scrsta = install_createscript(startaddr, stacksize, progsize, priority, scriptname)) < 0) { fprintf(stderr, "Error writing program script at %s: %s\n", argv[i+1], strerror(-scrsta)); return -EIO; } printf("Installed application of size %d bytes to program memory [%xh - %xh].\n", progsize, startaddr, startaddr + progsize); return 0; } fprintf(stderr, install_help, argv[0], argv[0]); return -1; }