f78626ac91ecdfe0eb0e9411f9e1091c461ebed4
[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 #include "ao-editaltos.h"
38
39
40 /*
41  * Read a 16-bit value from the target device with arbitrary
42  * alignment
43  */
44 static uint16_t
45 get_uint16_sl(stlink_t *sl, uint32_t addr)
46 {
47         const           uint8_t *data = sl->q_buf;
48         uint32_t        actual_addr;
49         int             off;
50         uint16_t        result;
51
52         sl->q_len = 0;
53
54
55         actual_addr = addr & ~3;
56         
57         stlink_read_mem32(sl, actual_addr, 8);
58
59         if (sl->q_len != 8)
60                 abort();
61
62         off = addr & 3;
63         result = data[off] | (data[off + 1] << 8);
64         return result;
65 }
66
67 static uint16_t
68 get_uint16(stlink_t *sl, struct cc_usb *cc, uint32_t addr)
69 {
70         uint16_t        result;
71         if (cc)
72                 result = ao_self_get_uint16(cc, addr);
73         else
74                 result = get_uint16_sl(sl, addr);
75         printf ("read 0x%08x = 0x%04x\n", addr, result);
76         return result;
77 }
78
79 /*
80  * Read a 32-bit value from the target device with arbitrary
81  * alignment
82  */
83 static uint32_t
84 get_uint32_sl(stlink_t *sl, uint32_t addr)
85 {
86         const           uint8_t *data = sl->q_buf;
87         uint32_t        actual_addr;
88         int             off;
89         uint32_t        result;
90
91         sl->q_len = 0;
92
93         printf ("read 0x%x\n", addr);
94
95         actual_addr = addr & ~3;
96         
97         stlink_read_mem32(sl, actual_addr, 8);
98
99         if (sl->q_len != 8)
100                 abort();
101
102         off = addr & 3;
103         result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24);
104         return result;
105 }
106
107 /*
108  * Read a 32-bit value from the target device with arbitrary
109  * alignment
110  */
111 static uint32_t
112 get_uint32(stlink_t *sl, struct cc_usb *cc, uint32_t addr)
113 {
114         uint32_t        result;
115
116         if (cc)
117                 result = ao_self_get_uint32(cc, addr);
118         else
119                 result = get_uint32_sl(sl, addr);
120         printf ("read 0x%08x = 0x%08x\n", addr, result);
121         return result;
122 }
123
124 /*
125  * Check to see if the target device has been
126  * flashed with a similar firmware image before
127  *
128  * This is done by looking for the same romconfig version,
129  * which should be at the same location as the linker script
130  * places this at 0x100 from the start of the rom section
131  */
132 static int
133 check_flashed(stlink_t *sl, struct cc_usb *cc)
134 {
135         uint16_t        romconfig_version = get_uint16(sl, cc, AO_ROMCONFIG_VERSION);
136         uint16_t        romconfig_check = get_uint16(sl, cc, AO_ROMCONFIG_CHECK);
137
138         if (romconfig_version != (uint16_t) ~romconfig_check) {
139                 fprintf (stderr, "Device has not been flashed before\n");
140                 return 0;
141         }
142         return 1;
143 }
144
145 static const struct option options[] = {
146         { .name = "stlink", .has_arg = 0, .val = 'S' },
147         { .name = "tty", .has_arg = 1, .val = 'T' },
148         { .name = "device", .has_arg = 1, .val = 'D' },
149         { .name = "cal", .has_arg = 1, .val = 'c' },
150         { .name = "serial", .has_arg = 1, .val = 's' },
151         { .name = "verbose", .has_arg = 1, .val = 'v' },
152         { 0, 0, 0, 0},
153 };
154
155 static void usage(char *program)
156 {
157         fprintf(stderr, "usage: %s [--stlink] [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] file.{elf,ihx}\n", program);
158         exit(1);
159 }
160
161 void
162 done(stlink_t *sl, struct cc_usb *cc, int code)
163 {
164         if (cc) {
165 /*              cc_usb_printf(cc, "a\n"); */
166                 cc_usb_close(cc);
167         }
168         if (sl) {
169                 stlink_reset(sl);
170                 stlink_run(sl);
171                 stlink_exit_debug_mode(sl);
172                 stlink_close(sl);
173         }
174         exit (code);
175 }
176
177 static int
178 ends_with(char *whole, char *suffix)
179 {
180         int whole_len = strlen(whole);
181         int suffix_len = strlen(suffix);
182
183         if (suffix_len > whole_len)
184                 return 0;
185         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
186 }
187
188 int
189 main (int argc, char **argv)
190 {
191         char                    *device = NULL;
192         char                    *filename;
193         Elf                     *e;
194         char                    *serial_end;
195         unsigned int            serial = 0;
196         char                    *serial_ucs2;
197         int                     serial_ucs2_len;
198         char                    serial_int[2];
199         unsigned int            s;
200         int                     i;
201         int                     string_num;
202         uint32_t                cal = 0;
203         char                    cal_int[4];
204         char                    *cal_end;
205         int                     c;
206         stlink_t                *sl = NULL;
207         int                     was_flashed = 0;
208         struct ao_hex_image     *load;
209         int                     tries;
210         struct cc_usb           *cc = NULL;
211         int                     use_stlink = 0;
212         char                    *tty = NULL;
213         int                     success;
214         int                     verbose = 0;
215         struct ao_sym           *file_symbols;
216         int                     num_file_symbols;
217
218         while ((c = getopt_long(argc, argv, "T:D:c:s:Sv:", options, NULL)) != -1) {
219                 switch (c) {
220                 case 'T':
221                         tty = optarg;
222                         break;
223                 case 'D':
224                         device = optarg;
225                         break;
226                 case 'c':
227                         cal = strtoul(optarg, &cal_end, 10);
228                         if (cal_end == optarg || *cal_end != '\0')
229                                 usage(argv[0]);
230                         break;
231                 case 's':
232                         serial = strtoul(optarg, &serial_end, 10);
233                         if (serial_end == optarg || *serial_end != '\0')
234                                 usage(argv[0]);
235                         break;
236                 case 'S':
237                         use_stlink = 1;
238                         break;
239                 case 'v':
240                         verbose++;
241                         break;
242                 default:
243                         usage(argv[0]);
244                         break;
245                 }
246         }
247
248         ao_verbose = verbose;
249
250         if (verbose > 1)
251                 ccdbg_add_debug(CC_DEBUG_BITBANG);
252
253         filename = argv[optind];
254         if (filename == NULL)
255                 usage(argv[0]);
256
257         if (ends_with (filename, ".elf")) {
258                 load = ao_load_elf(filename, &file_symbols, &num_file_symbols);
259         } else if (ends_with (filename, ".ihx")) {
260                 load = ao_hex_load(filename, &file_symbols, &num_file_symbols);
261         } else
262                 usage(argv[0]);
263
264         if (ao_editaltos_find_symbols(file_symbols, num_file_symbols, ao_symbols, ao_num_symbols))
265                 fprintf(stderr, "Cannot find required symbols\n");
266
267         if (use_stlink) {
268                 /* Connect to the programming dongle
269                  */
270         
271                 for (tries = 0; tries < 3; tries++) {
272                         if (device) {
273                                 sl = stlink_v1_open(50);
274                         } else {
275                                 sl = stlink_open_usb(50);
276                 
277                         }
278                         if (!sl) {
279                                 fprintf (stderr, "No STLink devices present\n");
280                                 done (sl, NULL, 1);
281                         }
282
283                         if (sl->chip_id != 0)
284                                 break;
285                         stlink_reset(sl);
286                         stlink_close(sl);
287                         sl = NULL;
288                 }
289                 if (!sl) {
290                         fprintf (stderr, "Debugger connection failed\n");
291                         exit(1);
292                 }
293
294                 /* Verify that the loaded image fits entirely within device flash
295                  */
296                 if (load->address < sl->flash_base ||
297                     sl->flash_base + sl->flash_size < load->address + load->length) {
298                         fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename,
299                                  load->address, load->address + load->length);
300                         done(sl, NULL, 1);
301                 }
302
303                 /* Enter debugging mode
304                  */
305                 if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE)
306                         stlink_exit_dfu_mode(sl);
307
308                 if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE)
309                         stlink_enter_swd_mode(sl);
310         } else {
311                 int     is_loader;
312                 int     tries;
313
314                 for (tries = 0; tries < 3; tries++) {
315                         char    *this_tty = tty;
316                         if (!this_tty)
317                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
318                         if (!this_tty)
319                                 this_tty = cc_usbdevs_find_by_arg(device, "MegaMetrum");
320                         if (!this_tty)
321                                 this_tty = getenv("ALTOS_TTY");
322                         if (!this_tty)
323                                 this_tty="/dev/ttyACM0";
324
325                         cc = cc_usb_open(this_tty);
326
327                         if (!cc)
328                                 exit(1);
329                         cc_usb_printf(cc, "v\n");
330                         is_loader = 0;
331                         for (;;) {
332                                 char    line[256];
333                                 cc_usb_getline(cc, line, sizeof(line));
334                                 if (!strncmp(line, "altos-loader", 12))
335                                         is_loader = 1;
336                                 if (!strncmp(line, "software-version", 16))
337                                         break;
338                         }
339                         if (is_loader)
340                                 break;
341                         printf ("rebooting to loader\n");
342                         cc_usb_printf(cc, "X\n");
343                         cc_usb_close(cc);
344                         sleep(1);
345                         cc = NULL;
346                 }
347                 if (!is_loader) {
348                         fprintf(stderr, "Cannot switch to boot loader\n");
349                         exit(1);
350                 }
351 #if 0
352                 {
353                         uint8_t check[256];
354                         int     i = 0;
355
356                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
357                         for (;;) {
358                                 uint8_t block[256];
359                                 putchar ('.');
360                                 if (++i == 40) {
361                                         putchar('\n');
362                                         i = 0;
363                                 }
364                                 fflush(stdout);
365                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
366                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
367                                 if (memcmp(block, check, 256) != 0) {
368                                         fprintf (stderr, "read differed\n");
369                                         exit(1);
370                                 }
371                         }
372                 }
373 #endif
374         }
375
376         /* Go fetch existing config values
377          * if available
378          */
379         was_flashed = check_flashed(sl, cc);
380
381         if (!serial) {
382                 if (!was_flashed) {
383                         fprintf (stderr, "Must provide serial number\n");
384                         done(sl, cc, 1);
385                 }
386                 serial = get_uint16(sl, cc, AO_SERIAL_NUMBER);
387                 if (!serial || serial == 0xffff) {
388                         fprintf (stderr, "Invalid existing serial %d\n", serial);
389                         done(sl, cc, 1);
390                 }
391         }
392
393         if (!cal && AO_RADIO_CAL && was_flashed) {
394                 cal = get_uint32(sl, cc, AO_RADIO_CAL);
395                 if (!cal || cal == 0xffffffff) {
396                         fprintf (stderr, "Invalid existing rf cal %d\n", cal);
397                         done(sl, cc, 1);
398                 }
399         }
400
401         if (!ao_editaltos(load, serial, cal))
402                 done(sl, cc, 1);
403
404         /* And flash the resulting image to the device
405          */
406         if (cc)
407                 success = ao_self_write(cc, load);
408         else
409                 success = (stlink_write_flash(sl, load->address, load->data, load->length) >= 0);
410                 
411         if (!success) {
412                 fprintf (stderr, "\"%s\": Write failed\n", filename);
413                 done(sl, cc, 1);
414         }
415
416         done(sl, cc, 0);
417 }