ao-tools: Split out USB loader to ao-usbload
[fw/altos] / ao-tools / ao-stmload / ao-stmload.c
index a471dcc47358b6d54a1dcefaae6769d73b750946..618ace21c7d91c03d7b3363be1b040314deae46c 100644 (file)
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
+#include <stdbool.h>
 #include "stlink-common.h"
+#include "ao-elf.h"
+#include "ccdbg.h"
+#include "cc.h"
+#include "ao-stmload.h"
+#include "ao-selfload.h"
+#include "ao-verbose.h"
+#include "ao-editaltos.h"
 
-#define AO_USB_DESC_STRING             3
-
-struct sym {
-       unsigned        addr;
-       char            *name;
-       int             required;
-} ao_symbols[] = {
-
-       { 0,    "ao_romconfig_version", 1 },
-#define AO_ROMCONFIG_VERSION   (ao_symbols[0].addr)
-
-       { 0,    "ao_romconfig_check",   1 },
-#define AO_ROMCONFIG_CHECK     (ao_symbols[1].addr)
-
-       { 0,    "ao_serial_number", 1 },
-#define AO_SERIAL_NUMBER       (ao_symbols[2].addr)
-
-       { 0,    "ao_usb_descriptors", 0 },
-#define AO_USB_DESCRIPTORS     (ao_symbols[3].addr)
-
-       { 0,    "ao_radio_cal", 0 },
-#define AO_RADIO_CAL           (ao_symbols[4].addr)
-};
-
-#define NUM_SYMBOLS            5
-#define NUM_REQUIRED_SYMBOLS   3
 
 /*
- * Look through the Elf file for the AltOS symbols
- * that can be adjusted before the image is written
- * to the device
- */
-static int
-find_symbols (Elf *e)
-{
-       Elf_Scn         *scn;
-       Elf_Data        *symbol_data = NULL;
-       GElf_Shdr       shdr;
-       GElf_Sym        sym;
-       int             i, symbol_count, s;
-       int             required = 0;
-       char            *symbol_name;
-
-       /*
-        * Find the symbols
-        */
-
-       scn = NULL;
-       while ((scn = elf_nextscn(e, scn)) != NULL) {
-               if (gelf_getshdr(scn, &shdr) != &shdr)
-                       return 0;
-
-               if (shdr.sh_type == SHT_SYMTAB) {
-                       symbol_data = elf_getdata(scn, NULL);
-                       symbol_count = shdr.sh_size / shdr.sh_entsize;
-                       break;
-               }
-       }
-
-       if (!symbol_data)
-               return 0;
-
-       for (i = 0; i < symbol_count; i++) {
-               gelf_getsym(symbol_data, i, &sym);
-
-               symbol_name = elf_strptr(e, shdr.sh_link, sym.st_name);
-
-               for (s = 0; s < NUM_SYMBOLS; s++)
-                       if (!strcmp (ao_symbols[s].name, symbol_name)) {
-                               int     t;
-                               ao_symbols[s].addr = sym.st_value;
-                               if (ao_symbols[s].required)
-                                       ++required;
-                       }
-       }
-
-       return required >= NUM_REQUIRED_SYMBOLS;
-}
-
-struct load {
-       uint32_t        addr;
-       uint32_t        len;
-       uint8_t         buf[0];
-};
-
-uint32_t round4(uint32_t a) {
-       return (a + 3) & ~3;
-}
-
-struct load *
-new_load (uint32_t addr, uint32_t len)
-{
-       struct load *new;
-
-       len = round4(len);
-       new = calloc (1, sizeof (struct load) + len);
-       if (!new)
-               abort();
-
-       new->addr = addr;
-       new->len = len;
-       return new;
-}
-
-void
-load_paste(struct load *into, struct load *from)
-{
-       if (from->addr < into->addr || into->addr + into->len < from->addr + from->len)
-               abort();
-
-       memcpy(into->buf + from->addr - into->addr, from->buf, from->len);
-}
-
-/*
- * Make a new load structure large enough to hold the old one and
- * the new data
+ * Read a 16-bit value from the target device with arbitrary
+ * alignment
  */
-struct load *
-expand_load(struct load *from, uint32_t addr, uint32_t len)
+static uint16_t
+get_uint16_sl(stlink_t *sl, uint32_t addr)
 {
-       struct load     *new;
-
-       if (from) {
-               uint32_t        from_last = from->addr + from->len;
-               uint32_t        last = addr + len;
-
-               if (addr > from->addr)
-                       addr = from->addr;
-               if (last < from_last)
-                       last = from_last;
-
-               len = last - addr;
-
-               if (addr == from->addr && len == from->len)
-                       return from;
-       }
-       new = new_load(addr, len);
-       if (from) {
-               load_paste(new, from);
-               free (from);
-       }
-       return new;
-}
+       const           uint8_t *data = sl->q_buf;
+       uint32_t        actual_addr;
+       int             off;
+       uint16_t        result;
 
-/*
- * Create a new load structure with data from the existing one
- * and the new data
- */
-struct load *
-load_write(struct load *from, uint32_t addr, uint32_t len, void *data)
-{
-       struct load     *new;
+       sl->q_len = 0;
 
-       new = expand_load(from, addr, len);
-       memcpy(new->buf + addr - new->addr, data, len);
-       return new;
-}
 
-/*
- * Construct a large in-memory block for all
- * of the loaded sections of the program
- */
-static struct load *
-get_load(Elf *e)
-{
-       Elf_Scn         *scn;
-       size_t          shstrndx;
-       GElf_Shdr       shdr;
-       Elf_Data        *data;
-       uint8_t         *buf;
-       char            *got_name;
-       size_t          nphdr;
-       int             p;
-       GElf_Phdr       phdr;
-       struct load     *load = NULL;
+       actual_addr = addr & ~3;
        
-       if (elf_getshdrstrndx(e, &shstrndx) < 0)
-               return 0;
-
-       if (elf_getphdrnum(e, &nphdr) < 0)
-               return 0;
-
-       /*
-        * As far as I can tell, all of the phdr sections should
-        * be flashed to memory
-        */
-       for (p = 0; p < nphdr; p++) {
-
-               /* Find this phdr */
-               gelf_getphdr(e, p, &phdr);
-
-               /* Get the associated file section */
-               scn = gelf_offscn(e, phdr.p_offset);
-
-               if (gelf_getshdr(scn, &shdr) != &shdr)
-                       abort();
-
-               data = elf_getdata(scn, NULL);
-
-               /* Write the section data into the memory block */
-               load = load_write(load, phdr.p_paddr, phdr.p_filesz, data->d_buf);
-       }
-       return load;
-}
-
-/*
- * Edit the to-be-written memory block
- */
-static int
-rewrite(struct load *load, unsigned addr, uint8_t *data, int len)
-{
-       int             i;
+       stlink_read_mem32(sl, actual_addr, 8);
 
-       if (addr < load->addr || load->addr + load->len < addr + len)
-               return 0;
+       if (sl->q_len != 8)
+               abort();
 
-       printf("rewrite %04x:", addr);
-       for (i = 0; i < len; i++)
-               printf (" %02x", load->buf[addr - load->addr + i]);
-       printf(" ->");
-       for (i = 0; i < len; i++)
-               printf (" %02x", data[i]);
-       printf("\n");
-       memcpy(load->buf + addr - load->addr, data, len);
+       off = addr & 3;
+       result = data[off] | (data[off + 1] << 8);
+       return result;
 }
 
-/*
- * Open the specified ELF file and
- * check for the symbols we need
- */
-
-Elf *
-ao_open_elf(char *name)
+static uint16_t
+get_uint16(stlink_t *sl, uint32_t addr)
 {
-       int             fd;
-       Elf             *e;
-       Elf_Scn         *scn;
-       Elf_Data        *symbol_data = NULL;
-       GElf_Shdr       shdr;
-       GElf_Sym        sym;
-       size_t          n, shstrndx, sz;
-       int             i, symbol_count, s;
-       int             required = 0;
-
-       if (elf_version(EV_CURRENT) == EV_NONE)
-               return NULL;
-
-       fd = open(name, O_RDONLY, 0);
-
-       if (fd < 0)
-               return NULL;
-
-       e = elf_begin(fd, ELF_C_READ, NULL);
-
-       if (!e)
-               return NULL;
-
-       if (elf_kind(e) != ELF_K_ELF)
-               return NULL;
-
-       if (elf_getshdrstrndx(e, &shstrndx) != 0)
-               return NULL;
-
-       if (!find_symbols(e)) {
-               fprintf (stderr, "Cannot find required symbols\n");
-               return NULL;
-       }
-
-       return e;
+       uint16_t        result;
+       result = get_uint16_sl(sl, addr);
+       printf ("read 0x%08x = 0x%04x\n", addr, result);
+       return result;
 }
 
 /*
@@ -303,7 +77,7 @@ ao_open_elf(char *name)
  * alignment
  */
 static uint32_t
-get_uint32(stlink_t *sl, uint32_t addr)
+get_uint32_sl(stlink_t *sl, uint32_t addr)
 {
        const           uint8_t *data = sl->q_buf;
        uint32_t        actual_addr;
@@ -323,35 +97,20 @@ get_uint32(stlink_t *sl, uint32_t addr)
 
        off = addr & 3;
        result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24);
-       printf ("read 0x%08x = 0x%08x\n", addr, result);
        return result;
 }
 
 /*
- * Read a 16-bit value from the target device with arbitrary
+ * Read a 32-bit value from the target device with arbitrary
  * alignment
  */
-static uint16_t
-get_uint16(stlink_t *sl, uint32_t addr)
+static uint32_t
+get_uint32(stlink_t *sl, uint32_t addr)
 {
-       const           uint8_t *data = sl->q_buf;
-       uint32_t        actual_addr;
-       int             off;
-       uint16_t        result;
-
-       sl->q_len = 0;
-
-
-       actual_addr = addr & ~3;
-       
-       stlink_read_mem32(sl, actual_addr, 8);
-
-       if (sl->q_len != 8)
-               abort();
+       uint32_t        result;
 
-       off = addr & 3;
-       result = data[off] | (data[off + 1] << 8);
-       printf ("read 0x%08x = 0x%04x\n", addr, result);
+       result = get_uint32_sl(sl, addr);
+       printf ("read 0x%08x = 0x%08x\n", addr, result);
        return result;
 }
 
@@ -377,30 +136,41 @@ check_flashed(stlink_t *sl)
 }
 
 static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
        { .name = "device", .has_arg = 1, .val = 'D' },
        { .name = "cal", .has_arg = 1, .val = 'c' },
        { .name = "serial", .has_arg = 1, .val = 's' },
+       { .name = "verbose", .has_arg = 1, .val = 'v' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--cal=<radio-cal>] [--serial=<serial>] file.elf\n", program);
+       fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] file.{elf,ihx}\n", program);
        exit(1);
 }
 
 void
 done(stlink_t *sl, int code)
 {
-       if (sl) {
-               stlink_reset(sl);
-               stlink_run(sl);
-               stlink_exit_debug_mode(sl);
-               stlink_close(sl);
-       }
+       stlink_reset(sl);
+       stlink_run(sl);
+       stlink_exit_debug_mode(sl);
+       stlink_close(sl);
        exit (code);
 }
 
+static int
+ends_with(char *whole, char *suffix)
+{
+       int whole_len = strlen(whole);
+       int suffix_len = strlen(suffix);
+
+       if (suffix_len > whole_len)
+               return 0;
+       return strcmp(whole + whole_len - suffix_len, suffix) == 0;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -419,12 +189,21 @@ main (int argc, char **argv)
        char                    cal_int[4];
        char                    *cal_end;
        int                     c;
-       stlink_t                *sl;
+       stlink_t                *sl = NULL;
        int                     was_flashed = 0;
-       struct load             *load;
-
-       while ((c = getopt_long(argc, argv, "D:c:s:", options, NULL)) != -1) {
+       struct ao_hex_image     *load;
+       int                     tries;
+       char                    *tty = NULL;
+       int                     success;
+       int                     verbose = 0;
+       struct ao_sym           *file_symbols;
+       int                     num_file_symbols;
+
+       while ((c = getopt_long(argc, argv, "T:D:c:s:v:", options, NULL)) != -1) {
                switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
                case 'D':
                        device = optarg;
                        break;
@@ -438,59 +217,66 @@ main (int argc, char **argv)
                        if (serial_end == optarg || *serial_end != '\0')
                                usage(argv[0]);
                        break;
+               case 'v':
+                       verbose++;
+                       break;
                default:
                        usage(argv[0]);
                        break;
                }
        }
 
+       ao_verbose = verbose;
+
+       if (verbose > 1)
+               ccdbg_add_debug(CC_DEBUG_BITBANG);
+
        filename = argv[optind];
        if (filename == NULL)
                usage(argv[0]);
 
-       /*
-        * Open the source file and load the symbols and
-        * flash data
-        */
-       
-       e = ao_open_elf(filename);
-       if (!e) {
-               fprintf(stderr, "Cannot open file \"%s\"\n", filename);
-               exit(1);
-       }
+       if (ends_with (filename, ".elf")) {
+               load = ao_load_elf(filename, &file_symbols, &num_file_symbols);
+       } else if (ends_with (filename, ".ihx")) {
+               load = ao_hex_load(filename, &file_symbols, &num_file_symbols);
+       } else
+               usage(argv[0]);
 
-       if (!find_symbols(e)) {
-               fprintf(stderr, "Cannot find symbols in \"%s\"\n", filename);
-               exit(1);
-       }
+       if (ao_editaltos_find_symbols(file_symbols, num_file_symbols, ao_symbols, ao_num_symbols))
+               fprintf(stderr, "Cannot find required symbols\n");
 
-       if (!(load = get_load(e))) {
-               fprintf(stderr, "Cannot find program data in \"%s\"\n", filename);
-               exit(1);
-       }
-               
        /* Connect to the programming dongle
         */
        
-       if (device) {
-               sl = stlink_v1_open(50);
-       } else {
-               sl = stlink_open_usb(50);
+       for (tries = 0; tries < 3; tries++) {
+               if (device) {
+                       sl = stlink_v1_open(50);
+               } else {
+                       sl = stlink_open_usb(50);
                
+               }
+               if (!sl) {
+                       fprintf (stderr, "No STLink devices present\n");
+                       done (sl, 1);
+               }
+
+               if (sl->chip_id != 0)
+                       break;
+               stlink_reset(sl);
+               stlink_close(sl);
+               sl = NULL;
        }
        if (!sl) {
-               fprintf (stderr, "No STLink devices present\n");
-               done (sl, 1);
+               fprintf (stderr, "Debugger connection failed\n");
+               exit(1);
        }
 
-       sl->verbose = 50;
-
        /* Verify that the loaded image fits entirely within device flash
         */
-       if (load->addr < sl->flash_base ||
-           sl->flash_base + sl->flash_size < load->addr + load->len) {
+       if (load->address < sl->flash_base ||
+           sl->flash_base + sl->flash_size < load->address + load->length) {
                fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename,
-                        load->addr, load->addr + load->len);
+                        load->address, load->address + load->length);
                done(sl, 1);
        }
 
@@ -527,69 +313,15 @@ main (int argc, char **argv)
                }
        }
 
-       /* Write the config values into the flash image
-        */
-
-       serial_int[0] = serial & 0xff;
-       serial_int[1] = (serial >> 8) & 0xff;
-
-       if (!rewrite(load, AO_SERIAL_NUMBER, serial_int, sizeof (serial_int))) {
-               fprintf(stderr, "Cannot rewrite serial integer at %08x\n",
-                       AO_SERIAL_NUMBER);
+       if (!ao_editaltos(load, serial, cal))
                done(sl, 1);
-       }
-
-       if (AO_USB_DESCRIPTORS) {
-               unsigned        usb_descriptors;
-               usb_descriptors = AO_USB_DESCRIPTORS - load->addr;
-               string_num = 0;
-
-               while (load->buf[usb_descriptors] != 0 && usb_descriptors < load->len) {
-                       if (load->buf[usb_descriptors+1] == AO_USB_DESC_STRING) {
-                               ++string_num;
-                               if (string_num == 4)
-                                       break;
-                       }
-                       usb_descriptors += load->buf[usb_descriptors];
-               }
-               if (usb_descriptors >= load->len || load->buf[usb_descriptors] == 0 ) {
-                       fprintf(stderr, "Cannot rewrite serial string at %08x\n", AO_USB_DESCRIPTORS);
-                       done(sl, 1);
-               }
-
-               serial_ucs2_len = load->buf[usb_descriptors] - 2;
-               serial_ucs2 = malloc(serial_ucs2_len);
-               if (!serial_ucs2) {
-                       fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len);
-                       done(sl, 1);
-               }
-               s = serial;
-               for (i = serial_ucs2_len / 2; i; i--) {
-                       serial_ucs2[i * 2 - 1] = 0;
-                       serial_ucs2[i * 2 - 2] = (s % 10) + '0';
-                       s /= 10;
-               }
-               if (!rewrite(load, usb_descriptors + 2 + load->addr, serial_ucs2, serial_ucs2_len)) {
-                       fprintf (stderr, "Cannot rewrite USB descriptor at %08x\n", AO_USB_DESCRIPTORS);
-                       done(sl, 1);
-               }
-       }
-
-       if (cal && AO_RADIO_CAL) {
-               cal_int[0] = cal & 0xff;
-               cal_int[1] = (cal >> 8) & 0xff;
-               cal_int[2] = (cal >> 16) & 0xff;
-               cal_int[3] = (cal >> 24) & 0xff;
-
-               if (!rewrite(load, AO_RADIO_CAL, cal_int, sizeof (cal_int))) {
-                       fprintf(stderr, "Cannot rewrite radio calibration at %08x\n", AO_RADIO_CAL);
-                       exit(1);
-               }
-       }
 
        /* And flash the resulting image to the device
         */
-       if (stlink_write_flash(sl, load->addr, load->buf, load->len) < 0) {
+
+       success = (stlink_write_flash(sl, load->address, load->data, load->length) >= 0);
+               
+       if (!success) {
                fprintf (stderr, "\"%s\": Write failed\n", filename);
                done(sl, 1);
        }