lm4flash.c 10.3 KB
Newer Older
Fabio Utzig's avatar
Fabio Utzig committed
1
/* lm4flash - TI Stellaris Launchpad ICDI flasher
Fabio Utzig's avatar
Fabio Utzig committed
2
 * Copyright (C) 2012 Fabio Utzig <fabio@utzig.net>
3
 * Copyright (C) 2012 Peter Stuge <peter@stuge.se>
Fabio Utzig's avatar
Fabio Utzig committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

Fabio Utzig's avatar
Fabio Utzig committed
20
21
#include <stdio.h>
#include <stdlib.h>
22
23
#include <stdint.h>
#include <string.h>
Fabio Utzig's avatar
Fabio Utzig committed
24
25
26
27
28

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

Fabio Utzig's avatar
Fabio Utzig committed
29
#include <libusb.h>
Fabio Utzig's avatar
Fabio Utzig committed
30

Fabio Utzig's avatar
Fabio Utzig committed
31
//#define DEBUG 1
32

33
34
35
#define ICDI_VID 0x1cbe
#define ICDI_PID 0x00fd

36
37
38
39
40
41
42
43
// Flash Patch and Breakpoint: see ARM Debug Interface V5 Architecture Specification
static const uint32_t FPB     = 0xe0002000;

// Rom Control: see Stellaris LM4F120H5QR Microcontroller Page XXX
static const uint32_t XXX1CTL  = 0x400fe000;
static const uint32_t XXX2CTL  = 0xe000edf0;
static const uint32_t XXX3CTL  = 0x400fe060;
static const uint32_t XXX4CTL  = 0x400fe1a0;
Fabio Utzig's avatar
Fabio Utzig committed
44
45

// Rom Control: see Stellaris LM4F120H5QR Microcontroller Page 531
46
static const uint32_t ROMCTL  = 0x400fe0f0;
Fabio Utzig's avatar
Fabio Utzig committed
47
48

// Flash Memory Address: see Stellaris LM4F120H5QR Microcontroller Page 497
49
50
51
52
53
54
static const uint32_t FMA     = 0x400fd000;

static const uint8_t INTERFACE_NR = 0x02;
static const uint8_t ENDPOINT_IN  = 0x83;
static const uint8_t ENDPOINT_OUT = 0x02;

55
56
57
58
59
60
61
62
63
64
65
66
#define START "$"
#define END "#"

#ifdef WIN32
#define snprintf _snprintf
#define SNPRINTF_OFFSET 1
#else
#define SNPRINTF_OFFSET 0
#endif

#define START_LEN strlen(START)
#define END_LEN (strlen(END) + 2)
67

68
#define FLASH_BLOCK_SIZE 512
69

70
71
72
73
74
75
76
77
78
79
80
/* Prefix + potentially every flash byte escaped */
#define BUF_SIZE 64 + 2*FLASH_BLOCK_SIZE

static uint8_t flash_block[FLASH_BLOCK_SIZE];
static union {
	char c[BUF_SIZE];
	uint8_t u8[BUF_SIZE];
	uint32_t u32[BUF_SIZE / 4];
} buf;

static uint32_t le32_to_cpu(const uint32_t x)
81
{
82
83
84
85
86
87
88
89
90
	union {
		uint8_t  b8[4];
		uint32_t b32;
	} _tmp;
	_tmp.b8[3] = x >> 24;
	_tmp.b8[2] = x >> 16;
	_tmp.b8[1] = x >> 8;
	_tmp.b8[0] = x & 0xff;
	return _tmp.b32;
91
92
}

93
94
#define cpu_to_le32 le32_to_cpu

95
96
97
98
99
100
static int send_command(libusb_device_handle *handle, int size)
{
	int transferred = 0;
	int retval;

#ifdef DEBUG
101
102
	int i, col;

Fabio Utzig's avatar
Fabio Utzig committed
103
104
	printf("buffer:\n");
	for (i = 0, col = 1; i < size; i++, col++) {
105
		printf("%02x ", buf.u8[i]);
Fabio Utzig's avatar
Fabio Utzig committed
106
107
108
		if (col == 16) { col = 0; printf("\n"); }
	}
	printf("\n");
109
110
#endif

111
	retval = libusb_bulk_transfer(handle, ENDPOINT_OUT, buf.u8, size, &transferred, 0);
112
113
114
115
116
117
118
119
120
121
	if (retval != 0 || size != transferred) {
		printf("Error transmitting data %d\n", retval);
	}

	return retval;
}

static int wait_response(libusb_device_handle *handle, int *size)
{
	int retval;
122
#ifdef DEBUG
123
	int i;
124
#endif
125

126
	retval = libusb_bulk_transfer(handle, ENDPOINT_IN, buf.u8, BUF_SIZE, size, 0);
127
128
129
130
131
132
133
134
	if (retval != 0) {
		printf("Error receiving data %d\n", retval);
	}

#ifdef DEBUG
	printf("wait_response: size=%d\n", *size);
	printf("buffer: ");
	for (i = 0; i < *size; i++)
135
		printf("0x%02x ", buf.u8[i]);
136
137
138
139
140
141
	printf("\n");
#endif

	return retval;
}

142
static int checksum_and_send(libusb_device_handle *handle, size_t idx)
143
{
144
145
146
	size_t i;
	uint8_t sum = 0;
	int retval, transfered;
147

148
149
	if (idx + SNPRINTF_OFFSET + END_LEN > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
150

151
152
	for (i = 1; i < idx; i++)
		sum += buf.u8[i];
153

154
	idx += sprintf(buf.c + idx, END "%02x", sum);
155
156

	retval = send_command(handle, idx);
157
158
	if (retval)
		return retval;
159

160
161
162
163
	/* wait for ack (+/-) */
	retval = wait_response(handle, &transfered);
	if (retval)
		return retval;
164

165
166
	if (transfered != 1 || buf.c[0] != '+')
		return LIBUSB_ERROR_OTHER;
167

168
169
	/* wait for command response */
	retval = wait_response(handle, &transfered);
170

171
	/* FIXME: validate transfered here? */
172
173
174
175

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
176

177
178
179
static int send_u8_hex(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
{
	size_t i, idx;
180

181
182
183
184
185
	/* Make sure that everything fits!
	 * START + prefix + hex bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + (2 * num_bytes) + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
186

187
	idx = sprintf(buf.c, START "%s", prefix);
188

189
190
	for (i = 0; bytes && i < num_bytes; i++)
		idx += sprintf(buf.c + idx, "%02x", bytes[i]);
191

192
	return checksum_and_send(handle, idx);
193
194
}

195
static int send_u8_binary(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
196
197
198
{
	size_t idx;

199
200
201
202
203
	/* Make sure that everything fits!
	 * START + prefix + bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + num_bytes + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
204

205
	idx = sprintf(buf.c, START "%s", prefix);
206

207
208
	memcpy(buf.c + idx, bytes, num_bytes);
	idx += num_bytes;
209

210
	return checksum_and_send(handle, idx);
211
212
}

213
static int send_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val, const char *suffix)
214
{
215
216
217
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s",
			prefix ? prefix : "", val,
			suffix ? suffix : "");
218

219
	return checksum_and_send(handle, idx);
220
221
}

222
static int send_u32_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val1, const char *infix, const uint32_t val2, const char *suffix)
223
{
224
225
226
227
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s%08x%s",
			prefix ? prefix : "", val1,
			infix ? infix : "", val2,
			suffix ? suffix : "");
228

229
230
	return checksum_and_send(handle, idx);
}
231
232


233
234
235
static int send_mem_write(libusb_device_handle *handle, const uint32_t addr, const uint32_t val)
{
	return send_u32_u32(handle, "X", addr, ",4:", val, NULL);
236
237
}

238
static int send_mem_read(libusb_device_handle *handle, const uint32_t addr, uint32_t *val)
Fabio Utzig's avatar
Fabio Utzig committed
239
{
240
241
242
	int retval = send_u32(handle, "x", addr, ",4");
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
243

244
245
	if (val)
		*val = le32_to_cpu(buf.u32[0]);
Fabio Utzig's avatar
Fabio Utzig committed
246

247
248
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
249

250
251
252
253
static int send_flash_erase(libusb_device_handle *handle, const uint32_t start, const uint32_t end)
{
	return send_u32_u32(handle, "vFlashErase:", start, ",", end, NULL);
}
Fabio Utzig's avatar
Fabio Utzig committed
254

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
static int send_flash_write(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i;
	char prefix[] = "vFlashWrite:12345678:";
	char by, rawbuf[1024], *buf = rawbuf;

	sprintf(strchr(prefix, ':') + 1, "%08x:", addr);

	for (i = 0; i < len; i++)
		switch (by = bytes[i]) {
		case '#':
		case '$':
		case '}':
			*buf++ = '}';
			by ^= 0x20;
			/* fall through */
		default:
			if (buf >= rawbuf + sizeof(rawbuf))
				return LIBUSB_ERROR_NO_MEM;

			*buf++ = by;
			break;
Fabio Utzig's avatar
Fabio Utzig committed
277
278
		}

279
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
280

281
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
282
283
}

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#define SEND_COMMAND(cmd) do { \
	int r = send_u8_hex(handle, "qRcmd,", (cmd), sizeof((cmd)) - 1); \
	if (r) \
		return r; \
} while (0)

#define SEND_STRING(str) do { \
	int r = send_u8_hex(handle, (str), NULL, 0); \
	if (r) \
		return r; \
} while (0)

#define MEM_WRITE(address, value) do { \
	int r = send_mem_write(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define MEM_READ(address, value) do { \
	int r = send_mem_read(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_ERASE(start, end) do { \
	int r = send_flash_erase(handle, (start), (end)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_WRITE(address, data, length) do { \
	int r = send_flash_write(handle, (address), (data), (length)); \
	if (r < (length)) \
		return LIBUSB_ERROR_OTHER; \
} while (0)

Fabio Utzig's avatar
Fabio Utzig committed
320
321
322
323
324
325

/*
 *  This flow is of commands is based on an USB capture of
 *  traffic between LM Flash Programmer and the Stellaris Launchpad
 *  when doing a firmware write
 */
326
static int write_firmware(libusb_device_handle *handle, FILE *f)
327
328
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
329
	uint32_t addr;
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
	size_t rdbytes;

	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
	MEM_WRITE(FPB, 0x3000000);
	MEM_READ(XXX1CTL, &val);
	MEM_READ(XXX1CTL + 4, &val);
	SEND_STRING("?");
	MEM_READ(XXX2CTL, &val);
	SEND_COMMAND("debug sreset");
	MEM_READ(XXX2CTL, &val);
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
	MEM_READ(XXX2CTL, &val);
	MEM_READ(XXX3CTL, &val);
	MEM_READ(XXX1CTL, &val);
	MEM_READ(XXX1CTL + 4, &val);
	MEM_READ(XXX1CTL + 8, &val);
	MEM_READ(XXX1CTL, &val);
	MEM_READ(XXX4CTL, &val);
Fabio Utzig's avatar
Fabio Utzig committed
351
352

	/* XXX: Repeated below, why? */
353
354
355
356
357
	MEM_WRITE(FMA, 0x0);
	MEM_READ(XXX2CTL, &val);
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
	MEM_READ(XXX2CTL, &val);
Fabio Utzig's avatar
Fabio Utzig committed
358

359
	MEM_WRITE(XXX2CTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
360
361

	/* XXX: this is the same sequence of the above commands? */
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
	MEM_WRITE(FMA, 0x200);
	MEM_READ(XXX2CTL, &val);
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
	MEM_READ(XXX2CTL, &val);

	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
	MEM_READ(XXX2CTL, &val);

	for (addr = 0; !feof(f); addr += sizeof(flash_block)) {
		rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

		if (rdbytes < sizeof(flash_block) && !feof(f)) {
			perror("fread");
			return LIBUSB_ERROR_OTHER;
		}

		FLASH_WRITE(addr, flash_block, rdbytes);
	}

	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
	MEM_WRITE(FPB, 0x3000000);
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	return 0;
393
}
Fabio Utzig's avatar
Fabio Utzig committed
394
395
396

int main(int argc, char *argv[])
{
397
398
399
	libusb_context *ctx = NULL;
	libusb_device_handle *handle = NULL;
	int retval = 1;
400
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
401
402
403

	if (argc < 2) {
		printf("usage: %s <binary-file>\n", argv[0]);
404
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
405
	}
Fabio Utzig's avatar
Fabio Utzig committed
406
407
408

	if (libusb_init(&ctx) != 0) {
		fprintf(stderr, "Error initializing libusb\n");
409
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
410
411
	}

412
	/* FIXME: should not be using this function call! */
413
	handle = libusb_open_device_with_vid_pid(ctx, ICDI_VID, ICDI_PID);
Fabio Utzig's avatar
Fabio Utzig committed
414
	if (!handle) {
415
416
		fprintf(stderr, "No ICDI device with USB VID:PID %04x:%04x found!\n",
				ICDI_VID, ICDI_PID);
417
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
418
419
	}

420
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
421
422
	if (retval != 0) {
		printf("Error claiming interface %d\n", retval);
423
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
424
425
	}

426
427
428
	f = fopen(argv[1], "rb");
	if (!f) {
		perror("fopen");
429
430
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
431
432
	}

433
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
434

435
done:
436
437
	if (f)
		fclose(f);
438
439
440
441
442
443
	if (handle)
		libusb_close(handle);
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
444
}