summaryrefslogblamecommitdiff
path: root/apps/system/install/install.c
blob: 2f11c643404db1528ec561d22e6f8868b4ebb373 (plain) (tree)

































































                                                                              
                                                               











                                                                                
                  









                                                                              


                                   


                                                 
                                                 







                                                         
                                   

                                                
 


























































































































































































                                                                                       
          







                                                 
                         









































































































                                                                                                    
/****************************************************************************
 * apps/system/install/install.c
 *
 *   Copyright (C) 2011 Uros Platise. All rights reserved.
 *   Author: Uros Platise <uros.platise@isotel.eu>
 *
 * 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.
 *
 ****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/progmem.h>
#include <sys/stat.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>


/****************************************************************************
 * Definitions
 ****************************************************************************/

#define ACTION_INSTALL              0x01
#define ACTION_REMOVE               0x00
#define ACTION_REINSTALL            0x03
#define ACTION_INSUFPARAM           0x80

#define INSTALL_PROGRAMBLOCKSIZE    1024


/****************************************************************************
 * Private data
 ****************************************************************************/

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 <required_stack_space>\n"
    "\t--priority <priority>\n"
    "\t--remove <dest-file>\tRemoves installed application\n"
    "\t--force\t\t\tReplaces existing installation\n"
    "\t--start <page>\t\tInstalls application at or after <page>\n"
    "\t--margin <pages>\tLeave some free space after the kernel (default 16)\n";
    
const char *install_script_text = 
    "# XIP stacksize=%x priority=%x size=%x\n";
    
const char *install_script_exec = 
    "exec 0x%x\n";
    
    
/****************************************************************************
 * Private functions
 ****************************************************************************/

int install_getstartpage(int startpage, int pagemargin, int desiredsize)
{
    uint16_t page = 0, stpage = 0xFFFF;
    uint16_t pagesize = 0;
    int      maxlen = -1;
    int      maxlen_start = 0xFFFF;
    int      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;
}


int install_programflash(int startaddr, const char *source)
{
    int  status;
    int  count;
    int  totalsize = 0;
    char *buf;
    FILE *fp;
    
    if ( (buf = malloc(INSTALL_PROGRAMBLOCKSIZE)) == NULL )
        return -errno;
    
    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;
}


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);
}


int install_getprogsize(const char *progname)
{
    struct stat fileinfo;
    
    if ( stat(progname, &fileinfo) < 0 ) 
        return -1;
        
    return fileinfo.st_size;
}


int install_alreadyexists(const char *scriptname)
{
    FILE *fp;
    
    if ( (fp=fopen(scriptname, "r"))==NULL )
        return 0;
        
    fclose(fp);
    return 1;
}


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;
}


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;
}


int install_remove(const char *scriptname)
{
    FILE    *fp;
    int      progsize, addr, freedsize;
    uint16_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;
}


/****************************************************************************
 * Start
 ****************************************************************************/

int install_main(int argc, char *argv[])
{
    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 (argv[i][0]=='-' && argv[i][1]=='-' && i<=argc) {
        
            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;
}