ao-usbload: Check target device name to avoid mis-flashing
[fw/altos] / ao-tools / ao-usbload / ao-usbload.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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <err.h>
20 #include <fcntl.h>
21 #include <gelf.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <sysexits.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <string.h>
29 #include <stdbool.h>
30 #include "ao-elf.h"
31 #include "ccdbg.h"
32 #include "cc-usb.h"
33 #include "cc.h"
34 #include "ao-usbload.h"
35 #include "ao-selfload.h"
36 #include "ao-verbose.h"
37 #include "ao-editaltos.h"
38
39 static uint16_t
40 get_uint16(struct cc_usb *cc, uint32_t addr)
41 {
42         uint16_t        result;
43         result = ao_self_get_uint16(cc, addr);
44         return result;
45 }
46
47 /*
48  * Read a 32-bit value from the target device with arbitrary
49  * alignment
50  */
51 static uint32_t
52 get_uint32(struct cc_usb *cc, uint32_t addr)
53 {
54         uint32_t        result;
55
56         result = ao_self_get_uint32(cc, addr);
57         return result;
58 }
59
60 /*
61  * Check to see if the target device has been
62  * flashed with a similar firmware image before
63  *
64  * This is done by looking for the same romconfig version,
65  * which should be at the same location as the linker script
66  * places this at 0x100 from the start of the rom section
67  */
68 static int
69 check_flashed(struct cc_usb *cc)
70 {
71         uint16_t        romconfig_version = get_uint16(cc, AO_ROMCONFIG_VERSION);
72         uint16_t        romconfig_check = get_uint16(cc, AO_ROMCONFIG_CHECK);
73
74         if (romconfig_version != (uint16_t) ~romconfig_check) {
75                 fprintf (stderr, "Device has not been flashed before\n");
76                 return 0;
77         }
78         return 1;
79 }
80
81 static const struct option options[] = {
82         { .name = "tty", .has_arg = 1, .val = 'T' },
83         { .name = "device", .has_arg = 1, .val = 'D' },
84         { .name = "raw", .has_arg = 0, .val = 'r' },
85         { .name = "cal", .has_arg = 1, .val = 'c' },
86         { .name = "serial", .has_arg = 1, .val = 's' },
87         { .name = "verbose", .has_arg = 1, .val = 'v' },
88         { .name = "wait", .has_arg = 0, .val = 'w' },
89         { .name = "force", .has_arg = 0, .val = 'f' },
90         { 0, 0, 0, 0},
91 };
92
93 static void usage(char *program)
94 {
95         fprintf(stderr, "usage: %s [--raw] [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] [--wait] [--force] file.{elf,ihx}\n", program);
96         exit(1);
97 }
98
99 void
100 done(struct cc_usb *cc, int code)
101 {
102 /*      cc_usb_printf(cc, "a\n"); */
103         cc_usb_close(cc);
104         exit (code);
105 }
106
107 static int
108 ends_with(char *whole, char *suffix)
109 {
110         int whole_len = strlen(whole);
111         int suffix_len = strlen(suffix);
112
113         if (suffix_len > whole_len)
114                 return 0;
115         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
116 }
117
118 static int
119 ucs2len(uint16_t *ucs2)
120 {
121         int     len = 0;
122         while (*ucs2++)
123                 len++;
124         return len;
125 }
126
127 int
128 putucs4(uint32_t c, FILE *file)
129 {
130         char d;
131         int     bits;
132
133              if (c <       0x80) { d = c;                         bits= -6; }
134         else if (c <      0x800) { d= ((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
135         else if (c <    0x10000) { d= ((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
136         else if (c <   0x200000) { d= ((c >> 18) & 0x07) | 0xF0;  bits= 12; }
137         else if (c <  0x4000000) { d= ((c >> 24) & 0x03) | 0xF8;  bits= 18; }
138         else if (c < 0x80000000) { d= ((c >> 30) & 0x01) | 0xFC;  bits= 24; }
139         else return EOF;
140
141         if (putc (d, file) < 0)
142                 return EOF;
143
144         for ( ; bits >= 0; bits-= 6)
145                 if (putc (((c >> bits) & 0x3F) | 0x80, file) < 0)
146                         return EOF;
147
148         return 0;
149 }
150
151 static void
152 putucs2str(uint16_t *ucs2str, FILE *file)
153 {
154         uint16_t        ucs2;
155
156         while ((ucs2 = *ucs2str++) != 0)
157                 putucs4(ucs2, file);
158 }
159
160 int
161 main (int argc, char **argv)
162 {
163         char                    *device = NULL;
164         char                    *filename;
165         Elf                     *e;
166         int                     raw = 0;
167         char                    *serial_end;
168         unsigned int            serial = 0;
169         char                    *serial_ucs2;
170         int                     serial_ucs2_len;
171         char                    serial_int[2];
172         unsigned int            s;
173         int                     i;
174         int                     string_num;
175         uint32_t                cal = 0;
176         char                    cal_int[4];
177         char                    *cal_end;
178         int                     c;
179         int                     was_flashed = 0;
180         struct ao_hex_image     *load;
181         int                     tries;
182         struct cc_usb           *cc = NULL;
183         char                    *tty = NULL;
184         int                     success;
185         int                     verbose = 0;
186         struct ao_sym           *file_symbols;
187         int                     num_file_symbols;
188         uint32_t                flash_base, flash_bound;
189         int                     has_flash_size = 0;
190         int                     force = 0;
191
192         while ((c = getopt_long(argc, argv, "wrT:D:c:s:v:", options, NULL)) != -1) {
193                 switch (c) {
194                 case 'T':
195                         tty = optarg;
196                         break;
197                 case 'D':
198                         device = optarg;
199                         break;
200                 case 'r':
201                         raw = 1;
202                         break;
203                 case 'w':
204                         cc_default_timeout = -1;
205                         break;
206                 case 'c':
207                         cal = strtoul(optarg, &cal_end, 10);
208                         if (cal_end == optarg || *cal_end != '\0')
209                                 usage(argv[0]);
210                         break;
211                 case 's':
212                         serial = strtoul(optarg, &serial_end, 10);
213                         if (serial_end == optarg || *serial_end != '\0')
214                                 usage(argv[0]);
215                         break;
216                 case 'v':
217                         verbose++;
218                         break;
219                 case 'f':
220                         force = 1;
221                         break;
222                 default:
223                         usage(argv[0]);
224                         break;
225                 }
226         }
227
228         ao_verbose = verbose;
229
230         if (verbose > 1)
231                 ccdbg_add_debug(CC_DEBUG_BITBANG);
232
233         filename = argv[optind];
234         if (filename == NULL)
235                 usage(argv[0]);
236
237         if (ends_with (filename, ".elf")) {
238                 load = ao_load_elf(filename, &file_symbols, &num_file_symbols);
239         } else if (ends_with (filename, ".ihx")) {
240                 load = ao_hex_load(filename, &file_symbols, &num_file_symbols);
241         } else
242                 usage(argv[0]);
243
244         if (!raw) {
245                 if (!ao_editaltos_find_symbols(file_symbols, num_file_symbols, ao_symbols, ao_num_symbols)) {
246                         fprintf(stderr, "Cannot find required symbols\n");
247                         usage(argv[0]);
248                 }
249         }
250
251         {
252                 int     is_loader;
253                 int     tries;
254
255                 for (tries = 0; tries < 3; tries++) {
256                         char    *this_tty = tty;
257                         if (!this_tty)
258                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
259                         if (!this_tty)
260                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleMega");
261                         if (!this_tty)
262                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleMetrum");
263                         if (!this_tty)
264                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleGPS");
265                         if (!this_tty)
266                                 this_tty = getenv("ALTOS_TTY");
267                         if (!this_tty)
268                                 this_tty="/dev/ttyACM0";
269
270                         cc = cc_usb_open(this_tty);
271
272                         if (!cc)
273                                 exit(1);
274                         cc_usb_printf(cc, "v\n");
275                         is_loader = 0;
276                         for (;;) {
277                                 char    line[256];
278                                 cc_usb_getline(cc, line, sizeof(line));
279                                 if (!strncmp(line, "altos-loader", 12))
280                                         is_loader = 1;
281                                 if (!strncmp(line, "flash-range", 11)) {
282                                         int i;
283                                         for (i = 11; i < strlen(line); i++)
284                                                 if (line[i] != ' ')
285                                                         break;
286                                         if (sscanf(line + i, "%x %x", &flash_base, &flash_bound) == 2)
287                                                 has_flash_size = 1;
288                                 }
289                                 if (!strncmp(line, "software-version", 16))
290                                         break;
291                         }
292                         if (is_loader)
293                                 break;
294                         printf ("rebooting to loader\n");
295                         cc_usb_printf(cc, "X\n");
296                         cc_usb_close(cc);
297                         sleep(1);
298                         cc = NULL;
299                 }
300                 if (!is_loader) {
301                         fprintf(stderr, "Cannot switch to boot loader\n");
302                         exit(1);
303                 }
304 #if 0
305                 {
306                         uint8_t check[256];
307                         int     i = 0;
308
309                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
310                         for (;;) {
311                                 uint8_t block[256];
312                                 putchar ('.');
313                                 if (++i == 40) {
314                                         putchar('\n');
315                                         i = 0;
316                                 }
317                                 fflush(stdout);
318                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
319                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
320                                 if (memcmp(block, check, 256) != 0) {
321                                         fprintf (stderr, "read differed\n");
322                                         exit(1);
323                                 }
324                         }
325                 }
326 #endif
327         }
328
329         /* If the device can tell us the size of flash, make sure
330          * the image fits in that
331          */
332         if (has_flash_size) {
333                 if (load->address < flash_base ||
334                     load->address + load->length > flash_bound)
335                 {
336                         fprintf(stderr, "Image does not fit on device.\n");
337                         fprintf(stderr, "  Image base:  %08x bounds %08x\n",
338                                 load->address, load->address + load->length);
339                         fprintf(stderr, "  Device base: %08x bounds %08x\n",
340                                 flash_base, flash_bound);
341                         done(cc, 1);
342                 }
343         }
344
345         if (!raw) {
346                 /* Go fetch existing config values
347                  * if available
348                  */
349                 was_flashed = check_flashed(cc);
350
351                 if (!serial) {
352                         if (!was_flashed) {
353                                 fprintf (stderr, "Must provide serial number\n");
354                                 done(cc, 1);
355                         }
356                         serial = get_uint16(cc, AO_SERIAL_NUMBER);
357                         if (!serial || serial == 0xffff) {
358                                 fprintf (stderr, "Invalid existing serial %d\n", serial);
359                                 done(cc, 1);
360                         }
361                 }
362
363                 if (!cal && AO_RADIO_CAL && was_flashed) {
364                         cal = get_uint32(cc, AO_RADIO_CAL);
365                         if (!cal || cal == 0xffffffff) {
366                                 fprintf (stderr, "Invalid existing rf cal %d\n", cal);
367                                 done(cc, 1);
368                         }
369                 }
370
371                 if (!force && was_flashed) {
372                         struct ao_usb_id        new_id, old_id;
373                         uint16_t                *new_product, *old_product;
374                         int                     new_len, old_len;
375
376                         if (!ao_heximage_usb_id(load, &new_id)) {
377                                 fprintf(stderr, "Can't get new USB id\n");
378                                 done(cc, 1);
379                         }
380
381                         if (!ao_self_get_usb_id(cc, &old_id)) {
382                                 fprintf(stderr, "Can't get old USB id\n");
383                                 done(cc, 1);
384                         }
385                         if (new_id.vid != old_id.vid || new_id.pid != old_id.pid) {
386                                 fprintf(stderr, "USB ID mismatch (device is %04x/%04x image is %04x/%04x)\n",
387                                         old_id.vid, old_id.pid, new_id.vid, new_id.pid);
388                                 done(cc, 1);
389                         }
390
391                         new_product = ao_heximage_usb_product(load);
392                         if (!new_product) {
393                                 fprintf(stderr, "Can't get new USB product name\n");
394                                 done(cc, 1);
395                         }
396                         old_product = ao_self_get_usb_product(cc);
397                         if (!old_product) {
398                                 fprintf(stderr, "Can't get existing USB product name\n");
399                                 done(cc, 1);
400                         }
401                         new_len = ucs2len(new_product);
402                         old_len = ucs2len(old_product);
403                         if (1 || new_len != old_len || memcmp(new_product, old_product, new_len * 2) != 0) {
404                                 fprintf(stderr, "USB product mismatch (device is ");
405                                 putucs2str(new_product, stderr);
406                                 fprintf(stderr, ", image is ");
407                                 putucs2str(old_product, stderr);
408                                 fprintf(stderr, ")\n");
409                                 done(cc, 1);
410                         }
411                 }
412
413                 if (!ao_editaltos(load, serial, cal))
414                         done(cc, 1);
415         }
416         done(cc, 0);
417
418         /* And flash the resulting image to the device
419          */
420         success = ao_self_write(cc, load);
421
422         if (!success) {
423                 fprintf (stderr, "\"%s\": Write failed\n", filename);
424                 done(cc, 1);
425         }
426
427         done(cc, 0);
428 }