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