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