1122 lines
31 KiB
C
1122 lines
31 KiB
C
/*
|
|
* Copyright (C) 2018 Massdrop Inc.
|
|
*
|
|
* This file is part of Massdrop Loader.
|
|
*
|
|
* Massdrop Loader is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Massdrop Loader is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Massdrop Loader. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mdloader_common.h"
|
|
#include "mdloader_parser.h"
|
|
|
|
char verbose;
|
|
char testmode;
|
|
char first_device;
|
|
int restart_after_program;
|
|
int hex_cols;
|
|
int hex_colw;
|
|
|
|
//SAM-BA Settings
|
|
mailbox_t initparams;
|
|
mailbox_t appletinfo; //Applet information reported by binary
|
|
appinfo_t appinfo; //Applet application information from end of applet binary
|
|
|
|
mcu_t mcus[] = {
|
|
//Name, Chip ID Chip ID, Program Memory, Data Memory, Program Addr, Data Addr
|
|
// Address (FLASH_SIZE) (HSRAM_SIZE) (FLASH_ADDR) (HSRAM_ADDR)
|
|
{ "SAMD51J18A", 0x41002018, 0x60060006, 0x40000, 0x20000, 0x00000000, 0x20000000},
|
|
{ "SAMD51J18A", 0x41002018, 0x60060305, 0x40000, 0x20000, 0x00000000, 0x20000000},
|
|
{ "SAMD51J18A", 0x41002018, 0x60060306, 0x40000, 0x20000, 0x00000000, 0x20000000},
|
|
{ "SAMD51J18A", 0x41002018, 0x60060005, 0x40000, 0x20000, 0x00000000, 0x20000000},
|
|
};
|
|
|
|
mcu_t *mcu; //Pointer to mcus entry if found
|
|
uint32_t bootloader_length;
|
|
|
|
//Guard against bootloader area writing
|
|
//Return 1 if attempt to write to bootloader area
|
|
//Return 0 otherwise
|
|
//The applet also performs this check
|
|
int check_bootloader_write_attempt(int addr)
|
|
{
|
|
if (addr < mcu->flash_addr + bootloader_length)
|
|
{
|
|
printf("Attempt to write to bootloader section denied!\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int read_error; //Cleared on read attempt and set to 1 if read fails
|
|
|
|
//Print bootloader's serial number if programmed in
|
|
//Return 1 on sucess, 0 on failure
|
|
int print_bootloader_serial(void)
|
|
{
|
|
int seroffset = read_word(bootloader_length - 4);
|
|
|
|
if (read_error)
|
|
{
|
|
printf("Serial Number: Read error!\n");
|
|
return 0;
|
|
}
|
|
|
|
if (verbose > 0) printf("Serial Number offset: 0x%08x\n", seroffset);
|
|
|
|
if (seroffset >= mcu->flash_addr + bootloader_length)
|
|
{
|
|
printf("Serial Number: Not programmed!\n");
|
|
return 0;
|
|
}
|
|
|
|
char *readbuf = recv_file(seroffset, SERIAL_MAX_LENGTH * 2);
|
|
|
|
if (!readbuf)
|
|
{
|
|
printf("Serial Number: Error retrieving!\n");
|
|
return 0;
|
|
}
|
|
|
|
printf("Serial Number: ");
|
|
int ind;
|
|
for (ind = 0; ind < SERIAL_MAX_LENGTH * 2; ind += 2)
|
|
printf("%c", readbuf[ind]);
|
|
printf("\n");
|
|
|
|
free(readbuf); //Free buffer created in recv_file
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Write mailbox data
|
|
//Return 1 on sucess, 0 on failure
|
|
int send_mail(mailbox_t *mail)
|
|
{
|
|
int byte;
|
|
int *pmail = (int *)mail;
|
|
if (verbose) printf("Sending mail {\n");
|
|
for (byte = 0; byte < sizeof(mailbox_t) / sizeof(int); byte++)
|
|
{
|
|
if (verbose) printf(" ");
|
|
write_word(appinfo.mail_addr+byte*sizeof(int), *pmail);
|
|
pmail++;
|
|
}
|
|
if (verbose) printf("}\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Read mailbox data
|
|
//Return 1 on sucess, 0 on failure
|
|
int read_mail(mailbox_t *mail)
|
|
{
|
|
int byte;
|
|
int *pmail = (int *)mail;
|
|
if (verbose) printf("Retrieving mail {\n");
|
|
for (byte = 0; byte < sizeof(mailbox_t) / sizeof(int); byte++)
|
|
{
|
|
if (verbose) printf(" ");
|
|
*pmail = read_word(appinfo.mail_addr+byte*sizeof(int));
|
|
pmail++;
|
|
}
|
|
if (verbose) printf("}\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Print mailbox data
|
|
//Return 1 on sucess, 0 on failure
|
|
int print_mail(mailbox_t *mail)
|
|
{
|
|
int arg;
|
|
int *pmail = (int *)mail;
|
|
|
|
printf("Mailbox contents:\n");
|
|
printf("Command: %i\n", mail->command);
|
|
printf("Status: %i\n", mail->status);
|
|
|
|
pmail += 2; //Bypass command and status
|
|
for (arg = 0; arg < (sizeof(mailbox_t) / sizeof(int)) - 2; arg++)
|
|
{
|
|
printf("Arg %i: %08X\n", arg, *pmail);
|
|
pmail++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Run applet command and wait for device response
|
|
//Return 1 on sucess, 0 on failure
|
|
int run_applet(mailbox_t *mail)
|
|
{
|
|
int retries;
|
|
int command;
|
|
|
|
command = mail->command;
|
|
|
|
if (command == APPLET_CMD_FULL_ERASE) retries = APPLET_RETRY_ERASE;
|
|
else retries = APPLET_RETRY_NORMAL;
|
|
|
|
goto_address(appinfo.load_addr); //Run the applet
|
|
|
|
if (verbose) printf("RUN: Command out: %08x\n", command);
|
|
do
|
|
{
|
|
slp(APPLET_WAIT_MS); //Allow applet to run
|
|
if (verbose) printf("RUN: Waiting on applet return\n");
|
|
mail->command = read_word(appinfo.mail_addr);
|
|
} while ((mail->command != ~command) && retries--);
|
|
|
|
if (verbose) print_mail(mail);
|
|
|
|
if (retries == -1)
|
|
{
|
|
if (verbose) printf("RUN: Error running applet\n");
|
|
return 0;
|
|
}
|
|
|
|
read_mail(mail);
|
|
if (mail->status == STATUS_OK)
|
|
{
|
|
if (verbose) printf("RUN: Applet return OK!\n");
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
printf("RUN: Applet return ERROR!\n");
|
|
if (verbose) print_mail(mail);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//Print a formatted hex/ascii listing of supplied data
|
|
void print_hex_listing(char *data, int binfilesize, int markbyte, int base_addr)
|
|
{
|
|
unsigned char *pbinfile = (unsigned char *)data;
|
|
int chrnum;
|
|
int binfileend = binfilesize;
|
|
int addr = base_addr;
|
|
int pf = (markbyte == 0);
|
|
unsigned char *ascii;
|
|
unsigned char *pascii = NULL;
|
|
unsigned int cols = hex_cols;
|
|
unsigned int colw = hex_colw;
|
|
|
|
if (cols < 1)
|
|
{
|
|
printf("Error: Hex listing column count invalid!\n");
|
|
return;
|
|
}
|
|
|
|
if (colw < 1)
|
|
{
|
|
printf("Error: Hex listing column width invalid!\n");
|
|
return;
|
|
}
|
|
|
|
ascii = (unsigned char *)calloc(cols * colw * sizeof(unsigned char) + 1, sizeof(unsigned char));
|
|
|
|
if (ascii == NULL)
|
|
{
|
|
printf("Error: Could not allocate memory for data listing!\n");
|
|
return;
|
|
}
|
|
|
|
pascii = ascii;
|
|
|
|
printf("\n");
|
|
|
|
for (chrnum = 0; chrnum < binfileend; chrnum++, pbinfile++)
|
|
{
|
|
if (chrnum)
|
|
{
|
|
if (chrnum % (cols * colw) == 0)
|
|
{
|
|
*pascii = 0;
|
|
printf("|%s|", ascii);
|
|
if (chrnum > markbyte && !pf)
|
|
{
|
|
printf(" @%i", markbyte % (cols * colw) + 1);
|
|
pf = 1;
|
|
}
|
|
printf("\n");
|
|
addr += cols * colw;
|
|
printf("%08X | ", addr);
|
|
pascii = ascii;
|
|
}
|
|
else if (chrnum % colw == 0) printf("%c", COLCHAR);
|
|
}
|
|
else
|
|
printf("%08X | ", addr);
|
|
|
|
if (*pbinfile >= 0x20 && *pbinfile <= 0x7E)
|
|
*pascii = *pbinfile;
|
|
else
|
|
*pascii = ' ';
|
|
pascii++;
|
|
|
|
if (*pbinfile < 0x10)
|
|
printf("0");
|
|
printf("%X%c", *pbinfile, TOKCHAR);
|
|
}
|
|
|
|
//Finish off a line with spaces if data ended early
|
|
while (chrnum % (cols * colw) != 0)
|
|
{
|
|
if (chrnum % colw == 0) printf("%c", COLCHAR);
|
|
|
|
*pascii = ' ';
|
|
pascii++;
|
|
|
|
printf(" %c", TOKCHAR);
|
|
|
|
chrnum++;
|
|
}
|
|
|
|
//Print last ascii line
|
|
*pascii = 0;
|
|
printf("|%s|", ascii);
|
|
if (chrnum > markbyte && !pf)
|
|
printf(" @%i", markbyte % (colw * cols) + 1);
|
|
|
|
printf("\n\n");
|
|
|
|
free(ascii);
|
|
}
|
|
|
|
int test_port(char *portname, char silent)
|
|
{
|
|
|
|
if (!open_port(portname, silent))
|
|
{
|
|
if (!silent) printf("Error: Could not open port! (Correct port?)\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!config_port())
|
|
{
|
|
if (!silent) printf("Error: Could not configure port! (Correct port?)\n");
|
|
close_port(silent);
|
|
return 0;
|
|
}
|
|
|
|
if (!set_normal_mode())
|
|
{
|
|
if (!silent) printf("Error: Could not communicate with device! (Correct port?)\n");
|
|
close_port(silent);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int test_mcu(char silent)
|
|
{
|
|
//Find MCU via device ID
|
|
int8_t mcu_index = -1;
|
|
int8_t mcu_max = sizeof(mcus) / sizeof(mcu_t);
|
|
int deviceid;
|
|
|
|
for (mcu_index = 0; mcu_index < mcu_max; mcu_index++)
|
|
{
|
|
mcu = (mcu_t *)&mcus[mcu_index];
|
|
deviceid = read_word(mcu->cidr_addr);
|
|
printf("Devid id read from mcu->cidr_addr: %x\n", deviceid);
|
|
if (read_error)
|
|
{
|
|
if (!silent && verbose) printf("Notice: Could not read device ID at %08X!\n", mcu->cidr_addr);
|
|
continue;
|
|
}
|
|
|
|
if (deviceid == mcu->cidr)
|
|
{
|
|
if (!silent && verbose) printf("Found supported device ID: %08X\n", deviceid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mcu_index == mcu_max)
|
|
{
|
|
if (!silent) printf("Error: Could not find matching device ID!\n");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Upper case any lower case characters in a string
|
|
void strlower(char *str)
|
|
{
|
|
char *c = str;
|
|
while (*c)
|
|
{
|
|
if (*c >= 'A' && *c <= 'Z') *c += 32;
|
|
c++;
|
|
}
|
|
}
|
|
|
|
//Lower case any upper case characters in a string
|
|
void strupper(char *str)
|
|
{
|
|
char *c = str;
|
|
while (*c)
|
|
{
|
|
if (*c >= 'a' && *c <= 'z') *c -= 32;
|
|
c++;
|
|
}
|
|
}
|
|
|
|
//Return file size of given file
|
|
int filesize(char *fname)
|
|
{
|
|
struct stat st;
|
|
stat(fname, &st);
|
|
return (int)st.st_size;
|
|
}
|
|
|
|
//Read byte from device
|
|
//Must check read_error for a read error after return
|
|
//Return unsigned byte from memory location
|
|
int read_byte(int addr)
|
|
{
|
|
return read_data(addr, 1);
|
|
}
|
|
|
|
//Read half word from device
|
|
//Must check read_error for a read error after return
|
|
//Return unsigned half word from memory location
|
|
int read_half_word(int addr)
|
|
{
|
|
return read_data(addr, 2);
|
|
}
|
|
|
|
//Read word from device
|
|
//Must check read_error for a read error after return
|
|
//Return unsigned word from memory location
|
|
int read_word(int addr)
|
|
{
|
|
return read_data(addr, 4);
|
|
}
|
|
|
|
//Write word to device
|
|
//Return 1 on success, 0 on failure
|
|
int write_word(int addr, int data)
|
|
{
|
|
return write_data(addr, 4, data);
|
|
}
|
|
|
|
//Write half word to device
|
|
//Return 1 on success, 0 on failure
|
|
int write_half_word(int addr, int data)
|
|
{
|
|
return write_data(addr, 2, data);
|
|
}
|
|
|
|
//Write byte to device
|
|
//Return 1 on success, 0 on failure
|
|
int write_byte(int addr, int data)
|
|
{
|
|
return write_data(addr, 1, data);
|
|
}
|
|
|
|
//Set terminal command mode
|
|
//Return 0 always
|
|
int set_terminal_mode(void)
|
|
{
|
|
//NOT SUPPORTING TERMINAL MODE
|
|
return 0;
|
|
}
|
|
|
|
//Display program version
|
|
void display_version(void)
|
|
{
|
|
printf(PROGRAM_NAME " %i.%02i\n", VERSION_MAJOR, VERSION_MINOR);
|
|
printf("\n");
|
|
}
|
|
|
|
//Display program copyright
|
|
void display_copyright(void)
|
|
{
|
|
printf(PROGRAM_NAME " Copyright (C) 2018 Massdrop Inc.\n");
|
|
printf("This program is Free Software and has ABSOLUTELY NO WARRANTY\n");
|
|
printf("\n");
|
|
}
|
|
|
|
//Display program help
|
|
void display_help(void)
|
|
{
|
|
printf("Usage: mdloader [options] ...\n");
|
|
printf(" -h --help Print this help message\n");
|
|
printf(" -v --verbose Print verbose messages\n");
|
|
printf(" -V --version Print version information\n");
|
|
printf(" -f --first Use first found device port as programming port\n");
|
|
printf(" -l --list Print valid attached devices for programming\n");
|
|
printf(" -p --port port Specify programming port\n");
|
|
printf(" -U --upload file Read firmware from device into <file>\n");
|
|
printf(" -a --addr address Read firmware starting from <address>\n");
|
|
printf(" -s --size size Read firmware size of <size>\n");
|
|
printf(" -D --download file Write firmware from <file> into device\n");
|
|
printf(" -t --test Test mode (download/upload writes disabled, upload outputs data to stdout, restart disabled)\n");
|
|
printf(" --cols count Hex listing column count <count> [%i]\n", COLS);
|
|
printf(" --colw width Hex listing column width <width> [%i]\n", COLW);
|
|
printf(" --restart Restart device after successful programming\n");
|
|
printf("\n");
|
|
}
|
|
|
|
#define SW_COLS 1000
|
|
#define SW_COLW 1001
|
|
|
|
//Program command line options
|
|
struct option long_options[] = {
|
|
//Flags
|
|
{ "restart", no_argument, &restart_after_program, 1 },
|
|
//Other
|
|
{ "verbose", no_argument, 0, 'v' },
|
|
{ "help", no_argument, 0, 'h' },
|
|
{ "version", no_argument, 0, 'V' },
|
|
{ "list", no_argument, 0, 'l' },
|
|
{ "first", no_argument, 0, 'f' },
|
|
{ "port", required_argument, 0, 'p' },
|
|
{ "download", required_argument, 0, 'D' },
|
|
{ "upload", required_argument, 0, 'U' },
|
|
{ "addr", required_argument, 0, 'a' },
|
|
{ "size", required_argument, 0, 's' },
|
|
{ "test", no_argument, 0, 't' },
|
|
{ "cols", required_argument, 0, SW_COLS },
|
|
{ "colw", required_argument, 0, SW_COLW },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
verbose = 0;
|
|
testmode = 0;
|
|
first_device = 0;
|
|
restart_after_program = 0;
|
|
hex_cols = COLS;
|
|
hex_colw = COLW;
|
|
|
|
display_version();
|
|
display_copyright();
|
|
|
|
int command = CMD_NONE;
|
|
char portname[500] = "";
|
|
char fname[1024] = "";
|
|
int upload_address = 0;
|
|
int upload_size = 0;
|
|
int upload_address_set = 0;
|
|
int upload_size_set = 0;
|
|
|
|
while (command != CMD_ABORT && command != CMD_HELP)
|
|
{
|
|
int c;
|
|
int option_index = 0;
|
|
int base;
|
|
|
|
c = getopt_long(argc, argv, "hvVlftp:D:U:a:s:", long_options, &option_index);
|
|
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
|
|
case 'h':
|
|
command = CMD_HELP;
|
|
break;
|
|
|
|
case 'V':
|
|
command = CMD_VERSION;
|
|
break;
|
|
|
|
case 'l':
|
|
if (command == CMD_NONE)
|
|
command = CMD_LISTDEV;
|
|
else
|
|
{
|
|
printf("Error: Another command conflicts with list!\n\n");
|
|
command = CMD_ABORT;
|
|
}
|
|
break;
|
|
|
|
case 'f':
|
|
first_device = 1;
|
|
break;
|
|
|
|
case 'p':
|
|
sprintf(portname, "%s", optarg);
|
|
break;
|
|
|
|
case 'U':
|
|
sprintf(fname, "%s", optarg);
|
|
if (command == CMD_NONE)
|
|
command = CMD_UPLOAD;
|
|
else
|
|
{
|
|
printf("Error: Another command conflicts with upload!\n\n");
|
|
command = CMD_ABORT;
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
strlower(optarg);
|
|
if (strstr(optarg, "0x") != NULL) base = 16;
|
|
else base = 10;
|
|
upload_address = (int)strtol(optarg, NULL, base);
|
|
upload_address_set = 1;
|
|
break;
|
|
|
|
case 's':
|
|
strlower(optarg);
|
|
if (strstr(optarg, "0x") != NULL) base = 16;
|
|
else base = 10;
|
|
upload_size = (int)strtol(optarg, NULL, base);
|
|
upload_size_set = 1;
|
|
break;
|
|
|
|
case 'D':
|
|
sprintf(fname, "%s", optarg);
|
|
if (command == CMD_NONE)
|
|
command = CMD_DOWNLOAD;
|
|
else
|
|
{
|
|
printf("Error: Another command conflicts with download!\n\n");
|
|
command = CMD_ABORT;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
testmode = 1;
|
|
break;
|
|
|
|
case SW_COLS:
|
|
hex_cols = atoi(optarg);
|
|
if (hex_cols < 1)
|
|
{
|
|
printf("Error: Hex listing column count must be greater than 0\n\n");
|
|
command = CMD_ABORT;
|
|
}
|
|
break;
|
|
|
|
case SW_COLW:
|
|
hex_colw = atoi(optarg);
|
|
if (hex_colw < 1)
|
|
{
|
|
printf("Error: Hex listing column width must be greater than 0\n\n");
|
|
command = CMD_ABORT;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
command = CMD_ABORT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (command == CMD_HELP || command == CMD_ABORT)
|
|
{
|
|
display_help();
|
|
goto exitProgram;
|
|
}
|
|
|
|
if (command == CMD_VERSION)
|
|
{
|
|
//Already displayed version by default
|
|
goto exitProgram;
|
|
}
|
|
|
|
if (command == CMD_LISTDEV)
|
|
{
|
|
list_devices(NULL);
|
|
goto exitProgram;
|
|
}
|
|
|
|
if (command == CMD_NONE)
|
|
{
|
|
if (!testmode && !restart_after_program) //Allow certain commands/flags through if alone
|
|
{
|
|
display_help();
|
|
goto exitProgram;
|
|
}
|
|
}
|
|
|
|
if (command == CMD_UPLOAD)
|
|
{
|
|
int upload_error = 0;
|
|
if (!upload_address_set)
|
|
{
|
|
printf("Error: Upload address must be set! Ex: --addr 0x4000\n");
|
|
upload_error = 1;
|
|
}
|
|
if (!upload_size_set)
|
|
{
|
|
printf("Error: Upload size must be set! Ex: --size 8192\n");
|
|
upload_error = 1;
|
|
}
|
|
|
|
if (upload_error)
|
|
{
|
|
goto exitProgram;
|
|
}
|
|
}
|
|
|
|
if (first_device)
|
|
{
|
|
//Set port according to first discovered device
|
|
|
|
int tries = 60;
|
|
|
|
printf("Scanning for device for %i seconds\n", tries);
|
|
while (tries)
|
|
{
|
|
printf(".");
|
|
fflush(stdout);
|
|
list_devices(portname);
|
|
if (*portname != 0)
|
|
{
|
|
printf("\n");
|
|
break; //Device port set
|
|
}
|
|
tries--;
|
|
slp(1000); //Sleep 1s
|
|
}
|
|
|
|
if (!tries)
|
|
{
|
|
printf("\n");
|
|
printf("Error: Could not find a valid device port!\n");
|
|
goto exitProgram;
|
|
}
|
|
}
|
|
|
|
if (*portname == 0)
|
|
{
|
|
printf("Error: No port specified!\n");
|
|
print_com_example();
|
|
goto exitProgram;
|
|
}
|
|
|
|
if (testmode)
|
|
{
|
|
printf("NOTICE: Test mode is active. Writes are disabled!\n\n");
|
|
}
|
|
|
|
mailbox_t mail;
|
|
|
|
if (!test_port(portname, FALSE)) goto exitProgram;
|
|
|
|
if (!test_mcu(FALSE)) goto closePort;
|
|
|
|
printf("Found MCU: %s\n", mcu->name);
|
|
|
|
print_bootloader_version();
|
|
if (verbose) printf("Device ID: %08X\n", mcu->cidr);
|
|
|
|
//Load applet
|
|
FILE *fIn;
|
|
char appletfname[128] = "";
|
|
strlower(mcu->name);
|
|
|
|
sprintf(appletfname, "applet-flash-%s.bin", mcu->name);
|
|
printf("Applet file: %s\n", appletfname);
|
|
|
|
fIn = fopen(appletfname, "rb");
|
|
if (!fIn)
|
|
{
|
|
printf("Error: Could not open applet file: %s\n", appletfname);
|
|
goto closePort;
|
|
}
|
|
else
|
|
{
|
|
char *appletbuf;
|
|
int filebytes;
|
|
int readbytes;
|
|
|
|
filebytes = filesize(appletfname);
|
|
if (filebytes == 0)
|
|
{
|
|
printf("Error: Applet file is empty!\n");
|
|
fclose(fIn);
|
|
goto closePort;
|
|
}
|
|
|
|
appletbuf = (char *)calloc(filebytes,1);
|
|
if (appletbuf == NULL)
|
|
{
|
|
printf("Error: Could not allocate memory for applet file!\n");
|
|
fclose(fIn);
|
|
goto closePort;
|
|
}
|
|
|
|
readbytes = (int)fread(appletbuf, 1, filebytes, fIn);
|
|
fclose(fIn);
|
|
|
|
if (readbytes != filebytes)
|
|
{
|
|
printf("Error: Applet read error!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (readbytes < sizeof(appinfo_t))
|
|
{
|
|
printf("Error: Applet binary too small!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
memcpy(&appinfo, appletbuf + readbytes - sizeof(appinfo_t), sizeof(appinfo_t));
|
|
if (appinfo.magic != 0x4142444D)
|
|
{
|
|
printf("Error: Applet info not found!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (verbose)
|
|
{
|
|
printf("Applet load address: %08X\n", appinfo.load_addr);
|
|
printf("Applet mail address: %08X\n", appinfo.mail_addr);
|
|
}
|
|
|
|
//printf("Applet data:\n");
|
|
//print_hex_listing(appletbuf, readbytes, 0, 0);
|
|
|
|
if (verbose) printf("Applet size: %i\n", readbytes);
|
|
|
|
if (!send_file(appinfo.load_addr, readbytes, appletbuf))
|
|
{
|
|
printf("Error: Could not send applet!\n");
|
|
free(appletbuf);
|
|
goto closePort;
|
|
}
|
|
|
|
free(appletbuf);
|
|
|
|
//printf("Applet data in RAM:\n");
|
|
//char *data_recv = recv_file(appinfo.load_addr, readbytes);
|
|
//if (data_recv)
|
|
//{
|
|
// print_hex_listing(data_recv, readbytes, 0, appinfo.load_addr);
|
|
// free(data_recv); //Free memory allocated in recv_file
|
|
//}
|
|
}
|
|
|
|
initparams.command = APPLET_CMD_INIT;
|
|
initparams.status = STATUS_BUSY;
|
|
initparams.argument.inputInit.bank = 0;
|
|
initparams.argument.inputInit.comType = USB_COM_TYPE;
|
|
initparams.argument.inputInit.traceLevel = 0;
|
|
|
|
send_mail(&initparams);
|
|
|
|
if (run_applet(&initparams) == 0)
|
|
{
|
|
printf("Error: Applet run error for init!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (verbose)
|
|
{
|
|
printf("Memory Size: %08X\n", initparams.argument.outputInit.memorySize);
|
|
printf("Buffer Addr: %08X\n", initparams.argument.outputInit.bufferAddress);
|
|
printf("Buffer Size: %08X\n", initparams.argument.outputInit.bufferSize);
|
|
printf("Lock Region Size: %04X\n", initparams.argument.outputInit.memoryInfo.lockRegionSize);
|
|
printf("Lock Region Bits: %04X\n", initparams.argument.outputInit.memoryInfo.numbersLockBits);
|
|
printf("Page Size: %08X\n", initparams.argument.outputInit.pageSize);
|
|
printf("Number of Pages: %08X\n", initparams.argument.outputInit.nbPages);
|
|
printf("App Start Page: %08X\n", initparams.argument.outputInit.appStartPage);
|
|
printf("\n");
|
|
}
|
|
|
|
appletinfo.command = APPLET_CMD_INFO;
|
|
appletinfo.status = STATUS_BUSY;
|
|
|
|
send_mail(&appletinfo);
|
|
|
|
if (run_applet(&appletinfo) == 0)
|
|
{
|
|
printf("Error: Applet run error for info!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
printf("Applet Version: %i\n", appletinfo.argument.outputInfo.version_number);
|
|
|
|
if (initparams.argument.outputInit.memorySize != mcu->flash_size)
|
|
{
|
|
printf("Error: MCU memory size mismatch! (Given %08X, Applet reported %08X)\n", mcu->flash_size, initparams.argument.outputInit.memorySize);
|
|
goto closePort;
|
|
}
|
|
|
|
bootloader_length = initparams.argument.outputInit.appStartPage * initparams.argument.outputInit.pageSize;
|
|
|
|
if (bootloader_length == 0)
|
|
{
|
|
printf("Error: Applet reported zero length bootloader!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (verbose)
|
|
{
|
|
printf("Bootloader length: 0x%X\n", bootloader_length);
|
|
print_bootloader_serial();
|
|
}
|
|
|
|
if (command == CMD_DOWNLOAD)
|
|
{
|
|
//Load application
|
|
data_t *data = NULL;
|
|
|
|
data = load_file(fname);
|
|
|
|
if (!data)
|
|
{
|
|
printf("Error: Could not parse file!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (data->addr < (mcu->flash_addr + bootloader_length))
|
|
{
|
|
printf("Error: Attempt to write to bootloader section!\n"); //This check is also performed in the loaded applet
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
|
|
if (data->size > mcu->flash_size - (mcu->flash_addr + bootloader_length))
|
|
{
|
|
printf("Error: Attempt to write outside memory bounds!\n");
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
|
|
char *pds = data->data;
|
|
char *pde = data->data + data->size;
|
|
|
|
int readbytes;
|
|
|
|
printf("Writing firmware... ");
|
|
if (testmode)
|
|
printf("(test mode disables writes) ");
|
|
|
|
readbytes = pde - pds < initparams.argument.outputInit.bufferSize ? pde - pds : initparams.argument.outputInit.bufferSize;
|
|
int memoryOffset = data->addr;
|
|
while (readbytes > 0)
|
|
{
|
|
//printf("Send firmware (%i):\n", readbytes);
|
|
//print_hex_listing(pds, readbytes, 0, 0);
|
|
|
|
if (!send_file(initparams.argument.outputInit.bufferAddress, readbytes, pds))
|
|
{
|
|
printf("\nError: Failed write to applet buffer!\n");
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
|
|
memset(&mail, 0, sizeof(mailbox_t));
|
|
|
|
//Note: Testmode will turn writes into reads
|
|
if (testmode)
|
|
{
|
|
mail.command = APPLET_CMD_READ;
|
|
mail.argument.inputRead.bufferAddr = initparams.argument.outputInit.bufferAddress;
|
|
mail.argument.inputRead.bufferSize = readbytes;
|
|
mail.argument.inputRead.memoryOffset = memoryOffset;
|
|
}
|
|
else
|
|
{
|
|
mail.command = APPLET_CMD_WRITE;
|
|
mail.argument.inputWrite.bufferAddr = initparams.argument.outputInit.bufferAddress;
|
|
mail.argument.inputWrite.bufferSize = readbytes;
|
|
mail.argument.inputWrite.memoryOffset = memoryOffset;
|
|
}
|
|
send_mail(&mail);
|
|
run_applet(&mail);
|
|
|
|
if (mail.status != STATUS_OK)
|
|
{
|
|
printf("\nError: Applet failed write!\n");
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
|
|
if (testmode)
|
|
{
|
|
if (mail.argument.outputRead.bytesRead != readbytes)
|
|
{
|
|
printf("\nError: Sent bytes != written bytes (%i != %i)\n", readbytes, mail.argument.outputRead.bytesRead);
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mail.argument.outputWrite.bytesWritten != readbytes)
|
|
{
|
|
printf("\nError: Sent bytes != written bytes (%i != %i)\n", readbytes, mail.argument.outputWrite.bytesWritten);
|
|
free_data(data);
|
|
goto closePort;
|
|
}
|
|
}
|
|
|
|
memoryOffset += readbytes;
|
|
|
|
pds += readbytes;
|
|
readbytes = pde - pds < initparams.argument.outputInit.bufferSize ? pde - pds : initparams.argument.outputInit.bufferSize;
|
|
}
|
|
|
|
free_data(data);
|
|
|
|
printf("Complete!\n");
|
|
|
|
if (restart_after_program)
|
|
jump_application();
|
|
}
|
|
|
|
if (command == CMD_UPLOAD)
|
|
{
|
|
if (upload_address + upload_size > mcu->flash_size)
|
|
{
|
|
printf("Error: Attempt to read outside memory bounds!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
if (upload_size <= 0)
|
|
{
|
|
printf("Error: Invalid read size!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
//Read memory
|
|
char *readbuffer;
|
|
char *preadbuffer;
|
|
|
|
readbuffer = (char *)malloc(upload_size);
|
|
preadbuffer = readbuffer;
|
|
|
|
if (!readbuffer)
|
|
{
|
|
printf("Error: Could not allocate memory for firmware read!\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Reading memory %08X - %08X... \n", upload_address, upload_address+upload_size-1);
|
|
//printf("Address: %08X\n", upload_address);
|
|
//printf("Size: %08X\n", upload_size);
|
|
|
|
int readbytes = upload_size;
|
|
int curbytes = initparams.argument.outputInit.bufferSize;
|
|
int memoryOffset = upload_address;
|
|
while (readbytes > 0)
|
|
{
|
|
if (curbytes > readbytes) curbytes = readbytes;
|
|
|
|
memset(&mail, 0, sizeof(mailbox_t));
|
|
|
|
mail.command = APPLET_CMD_READ;
|
|
mail.argument.inputRead.bufferAddr = initparams.argument.outputInit.bufferAddress;
|
|
mail.argument.inputRead.bufferSize = curbytes;
|
|
mail.argument.inputRead.memoryOffset = memoryOffset;
|
|
|
|
if (send_mail(&mail) != 1)
|
|
{
|
|
printf("\nError: Failed to send applet mail!\n");
|
|
free(readbuffer);
|
|
goto closePort;
|
|
}
|
|
|
|
if (run_applet(&mail) != 1)
|
|
{
|
|
printf("\nError: Failed to run applet!\n");
|
|
free(readbuffer);
|
|
goto closePort;
|
|
}
|
|
|
|
if (mail.status != STATUS_OK)
|
|
{
|
|
printf("\nError: Applet status not OK! [%i]\n", mail.status);
|
|
free(readbuffer);
|
|
goto closePort;
|
|
}
|
|
|
|
if (mail.argument.outputRead.bytesRead != curbytes)
|
|
{
|
|
printf("\nError: Sent bytes != written bytes (%i != %i)\n", curbytes, mail.argument.outputRead.bytesRead);
|
|
free(readbuffer);
|
|
goto closePort;
|
|
}
|
|
|
|
char *bufread = recv_file(initparams.argument.outputInit.bufferAddress, curbytes);
|
|
if (bufread)
|
|
{
|
|
memcpy(preadbuffer, bufread, curbytes);
|
|
preadbuffer += curbytes;
|
|
free(bufread);
|
|
}
|
|
else
|
|
{
|
|
printf("Error: Could not read data buffer!\n");
|
|
free(readbuffer);
|
|
goto closePort;
|
|
}
|
|
|
|
memoryOffset += curbytes;
|
|
|
|
readbytes -= curbytes;
|
|
}
|
|
|
|
if (testmode)
|
|
print_hex_listing(readbuffer, upload_size, 0, upload_address);
|
|
else
|
|
{
|
|
printf("Writing firmware to file... ");
|
|
FILE *fOut = fopen(fname, "wb");
|
|
|
|
if (!fOut)
|
|
{
|
|
printf("Failed!\n");
|
|
printf("Error: Could not open file for firmware read output!\n");
|
|
goto closePort;
|
|
}
|
|
|
|
fwrite(readbuffer, upload_size, sizeof(char), fOut);
|
|
|
|
fclose(fOut);
|
|
|
|
printf("Success!\n");
|
|
}
|
|
|
|
free(readbuffer);
|
|
|
|
printf("Complete!\n");
|
|
}
|
|
}
|
|
|
|
//Allow for restart flag with no command to restart the device
|
|
if (command == CMD_NONE && restart_after_program)
|
|
jump_application();
|
|
|
|
closePort:
|
|
|
|
close_port(FALSE);
|
|
|
|
exitProgram:
|
|
|
|
return 0;
|
|
}
|
|
|