ao-tools/ao-stmload: Add --verbose flag
[fw/altos] / ao-tools / ao-stmload / ao-stmload.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <err.h>
19 #include <fcntl.h>
20 #include <gelf.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <sysexits.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <string.h>
28 #include "stlink-common.h"
29 #include "ao-elf.h"
30 #include "ccdbg.h"
31 #include "cc-usb.h"
32 #include "cc.h"
33 #include "ao-stmload.h"
34
35 #define AO_USB_DESC_STRING              3
36
37 struct sym ao_symbols[] = {
38
39         { 0, AO_BOOT_APPLICATION_BASE + 0x100,  "ao_romconfig_version", 1 },
40 #define AO_ROMCONFIG_VERSION    (ao_symbols[0].addr)
41
42         { 0, AO_BOOT_APPLICATION_BASE + 0x102,  "ao_romconfig_check",   1 },
43 #define AO_ROMCONFIG_CHECK      (ao_symbols[1].addr)
44
45         { 0, AO_BOOT_APPLICATION_BASE + 0x104,  "ao_serial_number", 1 },
46 #define AO_SERIAL_NUMBER        (ao_symbols[2].addr)
47
48         { 0, AO_BOOT_APPLICATION_BASE + 0x108,  "ao_radio_cal", 0 },
49 #define AO_RADIO_CAL            (ao_symbols[3].addr)
50
51         { 0, AO_BOOT_APPLICATION_BASE + 0x10c,  "ao_usb_descriptors", 0 },
52 #define AO_USB_DESCRIPTORS      (ao_symbols[4].addr)
53 };
54
55 #define NUM_SYMBOLS             5
56 #define NUM_REQUIRED_SYMBOLS    3
57
58 int ao_num_symbols = NUM_SYMBOLS;
59 int ao_num_required_symbols = NUM_REQUIRED_SYMBOLS;
60
61 /*
62  * Edit the to-be-written memory block
63  */
64 static int
65 rewrite(struct hex_image *load, unsigned address, uint8_t *data, int length)
66 {
67         int             i;
68
69         if (address < load->address || load->address + load->length < address + length)
70                 return 0;
71
72         printf("rewrite %04x:", address);
73         for (i = 0; i < length; i++)
74                 printf (" %02x", load->data[address - load->address + i]);
75         printf(" ->");
76         for (i = 0; i < length; i++)
77                 printf (" %02x", data[i]);
78         printf("\n");
79         memcpy(load->data + address - load->address, data, length);
80 }
81
82 /*
83  * Read a 16-bit value from the USB target
84  */
85
86 static uint16_t
87 get_uint16_cc(struct cc_usb *cc, uint32_t addr)
88 {
89         struct hex_image        *hex = ao_self_read(cc, addr, 2);
90         uint16_t                v;
91         uint8_t                 *data;
92
93         if (!hex)
94                 return 0;
95         data = hex->data + addr - hex->address;
96         v = data[0] | (data[1] << 8);
97         free(hex);
98         return v;
99 }
100
101 static uint32_t
102 get_uint32_cc(struct cc_usb *cc, uint32_t addr)
103 {
104         struct hex_image        *hex = ao_self_read(cc, addr, 4);
105         uint32_t                v;
106         uint8_t                 *data;
107
108         if (!hex)
109                 return 0;
110         data = hex->data + addr - hex->address;
111         v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
112         free(hex);
113         return v;
114 }
115
116 /*
117  * Read a 16-bit value from the target device with arbitrary
118  * alignment
119  */
120 static uint16_t
121 get_uint16_sl(stlink_t *sl, uint32_t addr)
122 {
123         const           uint8_t *data = sl->q_buf;
124         uint32_t        actual_addr;
125         int             off;
126         uint16_t        result;
127
128         sl->q_len = 0;
129
130
131         actual_addr = addr & ~3;
132         
133         stlink_read_mem32(sl, actual_addr, 8);
134
135         if (sl->q_len != 8)
136                 abort();
137
138         off = addr & 3;
139         result = data[off] | (data[off + 1] << 8);
140         return result;
141 }
142
143 static uint16_t
144 get_uint16(stlink_t *sl, struct cc_usb *cc, uint32_t addr)
145 {
146         uint16_t        result;
147         if (cc)
148                 result = get_uint16_cc(cc, addr);
149         else
150                 result = get_uint16_sl(sl, addr);
151         printf ("read 0x%08x = 0x%04x\n", addr, result);
152         return result;
153 }
154
155 /*
156  * Read a 32-bit value from the target device with arbitrary
157  * alignment
158  */
159 static uint32_t
160 get_uint32_sl(stlink_t *sl, uint32_t addr)
161 {
162         const           uint8_t *data = sl->q_buf;
163         uint32_t        actual_addr;
164         int             off;
165         uint32_t        result;
166
167         sl->q_len = 0;
168
169         printf ("read 0x%x\n", addr);
170
171         actual_addr = addr & ~3;
172         
173         stlink_read_mem32(sl, actual_addr, 8);
174
175         if (sl->q_len != 8)
176                 abort();
177
178         off = addr & 3;
179         result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24);
180         return result;
181 }
182
183 /*
184  * Read a 32-bit value from the target device with arbitrary
185  * alignment
186  */
187 static uint32_t
188 get_uint32(stlink_t *sl, struct cc_usb *cc, uint32_t addr)
189 {
190         uint32_t        result;
191
192         if (cc)
193                 result = get_uint32_cc(cc, addr);
194         else
195                 result = get_uint32_sl(sl, addr);
196         printf ("read 0x%08x = 0x%08x\n", addr, result);
197         return result;
198 }
199
200 /*
201  * Check to see if the target device has been
202  * flashed with a similar firmware image before
203  *
204  * This is done by looking for the same romconfig version,
205  * which should be at the same location as the linker script
206  * places this at 0x100 from the start of the rom section
207  */
208 static int
209 check_flashed(stlink_t *sl, struct cc_usb *cc)
210 {
211         uint16_t        romconfig_version = get_uint16(sl, cc, AO_ROMCONFIG_VERSION);
212         uint16_t        romconfig_check = get_uint16(sl, cc, AO_ROMCONFIG_CHECK);
213
214         if (romconfig_version != (uint16_t) ~romconfig_check) {
215                 fprintf (stderr, "Device has not been flashed before\n");
216                 return 0;
217         }
218         return 1;
219 }
220
221 static const struct option options[] = {
222         { .name = "stlink", .has_arg = 0, .val = 'S' },
223         { .name = "tty", .has_arg = 1, .val = 'T' },
224         { .name = "device", .has_arg = 1, .val = 'D' },
225         { .name = "cal", .has_arg = 1, .val = 'c' },
226         { .name = "serial", .has_arg = 1, .val = 's' },
227         { .name = "verbose", .has_arg = 0, .val = 'v' },
228         { 0, 0, 0, 0},
229 };
230
231 static void usage(char *program)
232 {
233         fprintf(stderr, "usage: %s [--stlink] [--verbose] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] file.{elf,ihx}\n", program);
234         exit(1);
235 }
236
237 void
238 done(stlink_t *sl, struct cc_usb *cc, int code)
239 {
240         if (cc) {
241 /*              cc_usb_printf(cc, "a\n"); */
242                 cc_usb_close(cc);
243         }
244         if (sl) {
245                 stlink_reset(sl);
246                 stlink_run(sl);
247                 stlink_exit_debug_mode(sl);
248                 stlink_close(sl);
249         }
250         exit (code);
251 }
252
253 static int
254 ends_with(char *whole, char *suffix)
255 {
256         int whole_len = strlen(whole);
257         int suffix_len = strlen(suffix);
258
259         if (suffix_len > whole_len)
260                 return 0;
261         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
262 }
263
264 int
265 main (int argc, char **argv)
266 {
267         char                    *device = NULL;
268         char                    *filename;
269         Elf                     *e;
270         char                    *serial_end;
271         unsigned int            serial = 0;
272         char                    *serial_ucs2;
273         int                     serial_ucs2_len;
274         char                    serial_int[2];
275         unsigned int            s;
276         int                     i;
277         int                     string_num;
278         uint32_t                cal = 0;
279         char                    cal_int[4];
280         char                    *cal_end;
281         int                     c;
282         stlink_t                *sl = NULL;
283         int                     was_flashed = 0;
284         struct hex_image        *load;
285         int                     tries;
286         struct cc_usb           *cc = NULL;
287         int                     use_stlink = 0;
288         char                    *tty = NULL;
289         int                     success;
290         int                     verbose = 0;
291
292         while ((c = getopt_long(argc, argv, "T:D:c:s:Sv", options, NULL)) != -1) {
293                 switch (c) {
294                 case 'T':
295                         tty = optarg;
296                         break;
297                 case 'D':
298                         device = optarg;
299                         break;
300                 case 'c':
301                         cal = strtoul(optarg, &cal_end, 10);
302                         if (cal_end == optarg || *cal_end != '\0')
303                                 usage(argv[0]);
304                         break;
305                 case 's':
306                         serial = strtoul(optarg, &serial_end, 10);
307                         if (serial_end == optarg || *serial_end != '\0')
308                                 usage(argv[0]);
309                         break;
310                 case 'S':
311                         use_stlink = 1;
312                         break;
313                 case 'v':
314                         verbose++;
315                         break;
316                 default:
317                         usage(argv[0]);
318                         break;
319                 }
320         }
321
322         ao_self_verbose = verbose;
323
324         if (verbose > 1)
325                 ccdbg_add_debug(CC_DEBUG_BITBANG);
326
327         filename = argv[optind];
328         if (filename == NULL)
329                 usage(argv[0]);
330
331         if (ends_with (filename, ".elf")) {
332                 load = ao_load_elf(filename);
333         } else if (ends_with (filename, ".ihx")) {
334                 int     i;
335                 load = ccdbg_hex_load(filename);
336                 for (i = 0; i < ao_num_symbols; i++)
337                         ao_symbols[i].addr = ao_symbols[i].default_addr;
338         } else
339                 usage(argv[0]);
340
341         if (use_stlink) {
342                 /* Connect to the programming dongle
343                  */
344         
345                 for (tries = 0; tries < 3; tries++) {
346                         if (device) {
347                                 sl = stlink_v1_open(50);
348                         } else {
349                                 sl = stlink_open_usb(50);
350                 
351                         }
352                         if (!sl) {
353                                 fprintf (stderr, "No STLink devices present\n");
354                                 done (sl, NULL, 1);
355                         }
356
357                         if (sl->chip_id != 0)
358                                 break;
359                         stlink_reset(sl);
360                         stlink_close(sl);
361                         sl = NULL;
362                 }
363                 if (!sl) {
364                         fprintf (stderr, "Debugger connection failed\n");
365                         exit(1);
366                 }
367
368                 /* Verify that the loaded image fits entirely within device flash
369                  */
370                 if (load->address < sl->flash_base ||
371                     sl->flash_base + sl->flash_size < load->address + load->length) {
372                         fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename,
373                                  load->address, load->address + load->length);
374                         done(sl, NULL, 1);
375                 }
376
377                 /* Enter debugging mode
378                  */
379                 if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE)
380                         stlink_exit_dfu_mode(sl);
381
382                 if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE)
383                         stlink_enter_swd_mode(sl);
384         } else {
385                 int     is_loader;
386                 int     tries;
387
388                 for (tries = 0; tries < 3; tries++) {
389                         char    *this_tty = tty;
390                         if (!this_tty)
391                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
392                         if (!this_tty)
393                                 this_tty = cc_usbdevs_find_by_arg(device, "MegaMetrum");
394                         if (!this_tty)
395                                 this_tty = getenv("ALTOS_TTY");
396                         if (!this_tty)
397                                 this_tty="/dev/ttyACM0";
398
399                         cc = cc_usb_open(this_tty);
400
401                         if (!cc)
402                                 exit(1);
403                         cc_usb_printf(cc, "v\n");
404                         is_loader = 0;
405                         for (;;) {
406                                 char    line[256];
407                                 cc_usb_getline(cc, line, sizeof(line));
408                                 if (!strncmp(line, "altos-loader", 12))
409                                         is_loader = 1;
410                                 if (!strncmp(line, "software-version", 16))
411                                         break;
412                         }
413                         if (is_loader)
414                                 break;
415                         printf ("rebooting to loader\n");
416                         cc_usb_printf(cc, "X\n");
417                         cc_usb_close(cc);
418                         sleep(1);
419                         cc = NULL;
420                 }
421                 if (!is_loader) {
422                         fprintf(stderr, "Cannot switch to boot loader\n");
423                         exit(1);
424                 }
425 #if 0
426                 {
427                         uint8_t check[256];
428                         int     i = 0;
429
430                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
431                         for (;;) {
432                                 uint8_t block[256];
433                                 putchar ('.');
434                                 if (++i == 40) {
435                                         putchar('\n');
436                                         i = 0;
437                                 }
438                                 fflush(stdout);
439                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
440                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
441                                 if (memcmp(block, check, 256) != 0) {
442                                         fprintf (stderr, "read differed\n");
443                                         exit(1);
444                                 }
445                         }
446                 }
447 #endif
448         }
449
450         /* Go fetch existing config values
451          * if available
452          */
453         was_flashed = check_flashed(sl, cc);
454
455         if (!serial) {
456                 if (!was_flashed) {
457                         fprintf (stderr, "Must provide serial number\n");
458                         done(sl, cc, 1);
459                 }
460                 serial = get_uint16(sl, cc, AO_SERIAL_NUMBER);
461                 if (!serial || serial == 0xffff) {
462                         fprintf (stderr, "Invalid existing serial %d\n", serial);
463                         done(sl, cc, 1);
464                 }
465         }
466
467         if (!cal && AO_RADIO_CAL && was_flashed) {
468                 cal = get_uint32(sl, cc, AO_RADIO_CAL);
469                 if (!cal || cal == 0xffffffff) {
470                         fprintf (stderr, "Invalid existing rf cal %d\n", cal);
471                         done(sl, cc, 1);
472                 }
473         }
474
475         /* Write the config values into the flash image
476          */
477
478         serial_int[0] = serial & 0xff;
479         serial_int[1] = (serial >> 8) & 0xff;
480
481         if (!rewrite(load, AO_SERIAL_NUMBER, serial_int, sizeof (serial_int))) {
482                 fprintf(stderr, "Cannot rewrite serial integer at %08x\n",
483                         AO_SERIAL_NUMBER);
484                 done(sl, cc, 1);
485         }
486
487         if (AO_USB_DESCRIPTORS) {
488                 uint32_t        usb_descriptors = AO_USB_DESCRIPTORS - load->address;
489                 string_num = 0;
490
491                 while (load->data[usb_descriptors] != 0 && usb_descriptors < load->length) {
492                         if (load->data[usb_descriptors+1] == AO_USB_DESC_STRING) {
493                                 ++string_num;
494                                 if (string_num == 4)
495                                         break;
496                         }
497                         usb_descriptors += load->data[usb_descriptors];
498                 }
499                 if (usb_descriptors >= load->length || load->data[usb_descriptors] == 0 ) {
500                         fprintf(stderr, "Cannot rewrite serial string at %08x\n", AO_USB_DESCRIPTORS);
501                         done(sl, cc, 1);
502                 }
503
504                 serial_ucs2_len = load->data[usb_descriptors] - 2;
505                 serial_ucs2 = malloc(serial_ucs2_len);
506                 if (!serial_ucs2) {
507                         fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len);
508                         done(sl, cc, 1);
509                 }
510                 s = serial;
511                 for (i = serial_ucs2_len / 2; i; i--) {
512                         serial_ucs2[i * 2 - 1] = 0;
513                         serial_ucs2[i * 2 - 2] = (s % 10) + '0';
514                         s /= 10;
515                 }
516                 if (!rewrite(load, usb_descriptors + 2 + load->address, serial_ucs2, serial_ucs2_len)) {
517                         fprintf (stderr, "Cannot rewrite USB descriptor at %08x\n", AO_USB_DESCRIPTORS);
518                         done(sl, cc, 1);
519                 }
520         }
521
522         if (cal && AO_RADIO_CAL) {
523                 cal_int[0] = cal & 0xff;
524                 cal_int[1] = (cal >> 8) & 0xff;
525                 cal_int[2] = (cal >> 16) & 0xff;
526                 cal_int[3] = (cal >> 24) & 0xff;
527
528                 if (!rewrite(load, AO_RADIO_CAL, cal_int, sizeof (cal_int))) {
529                         fprintf(stderr, "Cannot rewrite radio calibration at %08x\n", AO_RADIO_CAL);
530                         exit(1);
531                 }
532         }
533
534         /* And flash the resulting image to the device
535          */
536         if (cc)
537                 success = ao_self_write(cc, load);
538         else
539                 success = (stlink_write_flash(sl, load->address, load->data, load->length) >= 0);
540                 
541         if (!success) {
542                 fprintf (stderr, "\"%s\": Write failed\n", filename);
543                 done(sl, cc, 1);
544         }
545
546         done(sl, cc, 0);
547 }