a553f267 |
1 | //----------------------------------------------------------------------------- |
8fe1a992 |
2 | // Copyright (C) 2010 Hector Martin "marcan" <marcan@marcansoft.com> |
3 | // |
a553f267 |
4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, |
5 | // at your option, any later version. See the LICENSE.txt file for the text of |
6 | // the license. |
7 | //----------------------------------------------------------------------------- |
8fe1a992 |
8 | // ELF file flasher |
a553f267 |
9 | //----------------------------------------------------------------------------- |
10 | |
6e4d4ee6 |
11 | #include <stdio.h> |
6e4d4ee6 |
12 | #include <string.h> |
83a9b236 |
13 | #include <stdlib.h> |
43534cba |
14 | #include <inttypes.h> |
acf0582d |
15 | #include <unistd.h> |
125a98a1 |
16 | #include "proxmark3.h" |
acf0582d |
17 | #include "util.h" |
6e4d4ee6 |
18 | #include "flash.h" |
2cab856f |
19 | #include "elf.h" |
8fe1a992 |
20 | #include "proxendian.h" |
28fdb04f |
21 | #include "usb_cmd.h" |
22 | |
23 | void SendCommand(UsbCommand* txcmd); |
24 | void ReceiveCommand(UsbCommand* rxcmd); |
25 | void CloseProxmark(); |
26 | int OpenProxmark(size_t i); |
6e4d4ee6 |
27 | |
0ae6234a |
28 | // FIXME: what the fuckity fuck |
8fe1a992 |
29 | unsigned int current_command = CMD_UNKNOWN; |
30 | |
31 | #define FLASH_START 0x100000 |
32 | #define FLASH_SIZE (256*1024) |
33 | #define FLASH_END (FLASH_START + FLASH_SIZE) |
34 | #define BOOTLOADER_SIZE 0x2000 |
35 | #define BOOTLOADER_END (FLASH_START + BOOTLOADER_SIZE) |
36 | |
28fdb04f |
37 | #define BLOCK_SIZE 0x200 |
8fe1a992 |
38 | |
39 | static const uint8_t elf_ident[] = { |
40 | 0x7f, 'E', 'L', 'F', |
41 | ELFCLASS32, |
42 | ELFDATA2LSB, |
43 | EV_CURRENT |
44 | }; |
45 | |
46 | // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent |
47 | // unaligned segments if needed |
48 | static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) |
7fe9b0b7 |
49 | { |
8fe1a992 |
50 | Elf32_Phdr *phdr = phdrs; |
51 | flash_seg_t *seg; |
52 | uint32_t last_end = 0; |
53 | |
54 | ctx->segments = malloc(sizeof(flash_seg_t) * num_phdrs); |
55 | if (!ctx->segments) { |
56 | fprintf(stderr, "Out of memory\n"); |
57 | return -1; |
58 | } |
59 | ctx->num_segs = 0; |
60 | seg = ctx->segments; |
61 | |
62 | fprintf(stderr, "Loading usable ELF segments:\n"); |
63 | for (int i = 0; i < num_phdrs; i++) { |
64 | if (le32(phdr->p_type) != PT_LOAD) { |
65 | phdr++; |
66 | continue; |
67 | } |
68 | uint32_t vaddr = le32(phdr->p_vaddr); |
69 | uint32_t paddr = le32(phdr->p_paddr); |
70 | uint32_t filesz = le32(phdr->p_filesz); |
71 | uint32_t memsz = le32(phdr->p_memsz); |
72 | uint32_t offset = le32(phdr->p_offset); |
73 | uint32_t flags = le32(phdr->p_flags); |
74 | if (!filesz) { |
75 | phdr++; |
76 | continue; |
77 | } |
78 | fprintf(stderr, "%d: V 0x%08x P 0x%08x (0x%08x->0x%08x) [%c%c%c] @0x%x\n", |
79 | i, vaddr, paddr, filesz, memsz, |
80 | flags & PF_R ? 'R' : ' ', |
81 | flags & PF_W ? 'W' : ' ', |
82 | flags & PF_X ? 'X' : ' ', |
83 | offset); |
84 | if (filesz != memsz) { |
85 | fprintf(stderr, "Error: PHDR file size does not equal memory size\n" |
86 | "(DATA+BSS PHDRs do not make sense on ROM platforms!)\n"); |
87 | return -1; |
88 | } |
89 | if (paddr < last_end) { |
90 | fprintf(stderr, "Error: PHDRs not sorted or overlap\n"); |
91 | return -1; |
92 | } |
93 | if (paddr < FLASH_START || (paddr+filesz) > FLASH_END) { |
94 | fprintf(stderr, "Error: PHDR is not contained in Flash\n"); |
95 | return -1; |
96 | } |
97 | if (vaddr >= FLASH_START && vaddr < FLASH_END && (flags & PF_W)) { |
98 | fprintf(stderr, "Error: Flash VMA segment is writable\n"); |
99 | return -1; |
100 | } |
101 | |
102 | uint8_t *data; |
103 | // make extra space if we need to move the data forward |
104 | data = malloc(filesz + BLOCK_SIZE); |
105 | if (!data) { |
106 | fprintf(stderr, "Out of memory\n"); |
107 | return -1; |
108 | } |
109 | if (fseek(fd, offset, SEEK_SET) < 0 || fread(data, 1, filesz, fd) != filesz) { |
110 | fprintf(stderr, "Error while reading PHDR payload\n"); |
111 | free(data); |
112 | return -1; |
113 | } |
114 | |
115 | uint32_t block_offset = paddr & (BLOCK_SIZE-1); |
116 | if (block_offset) { |
117 | if (ctx->num_segs) { |
118 | flash_seg_t *prev_seg = seg - 1; |
119 | uint32_t this_end = paddr + filesz; |
120 | uint32_t this_firstblock = paddr & ~(BLOCK_SIZE-1); |
121 | uint32_t prev_lastblock = (last_end - 1) & ~(BLOCK_SIZE-1); |
122 | |
123 | if (this_firstblock == prev_lastblock) { |
124 | uint32_t new_length = this_end - prev_seg->start; |
125 | uint32_t this_offset = paddr - prev_seg->start; |
126 | uint32_t hole = this_offset - prev_seg->length; |
127 | uint8_t *new_data = malloc(new_length); |
128 | if (!new_data) { |
129 | fprintf(stderr, "Out of memory\n"); |
130 | free(data); |
131 | return -1; |
132 | } |
133 | memset(new_data, 0xff, new_length); |
134 | memcpy(new_data, prev_seg->data, prev_seg->length); |
135 | memcpy(new_data + this_offset, data, filesz); |
136 | fprintf(stderr, "Note: Extending previous segment from 0x%x to 0x%x bytes\n", |
137 | prev_seg->length, new_length); |
138 | if (hole) |
139 | fprintf(stderr, "Note: 0x%x-byte hole created\n", hole); |
140 | free(data); |
141 | free(prev_seg->data); |
142 | prev_seg->data = new_data; |
143 | prev_seg->length = new_length; |
144 | last_end = this_end; |
145 | phdr++; |
146 | continue; |
147 | } |
148 | } |
149 | fprintf(stderr, "Warning: segment does not begin on a block boundary, will pad\n"); |
150 | memmove(data + block_offset, data, filesz); |
151 | memset(data, 0xFF, block_offset); |
152 | filesz += block_offset; |
153 | paddr -= block_offset; |
154 | } |
155 | |
156 | seg->data = data; |
157 | seg->start = paddr; |
158 | seg->length = filesz; |
159 | seg++; |
160 | ctx->num_segs++; |
161 | |
162 | last_end = paddr + filesz; |
163 | phdr++; |
164 | } |
165 | return 0; |
6e4d4ee6 |
166 | } |
167 | |
8fe1a992 |
168 | // Sanity check segments and check for bootloader writes |
169 | static int check_segs(flash_file_t *ctx, int can_write_bl) { |
170 | for (int i = 0; i < ctx->num_segs; i++) { |
171 | flash_seg_t *seg = &ctx->segments[i]; |
172 | |
173 | if (seg->start & (BLOCK_SIZE-1)) { |
174 | fprintf(stderr, "Error: Segment is not aligned\n"); |
175 | return -1; |
176 | } |
177 | if (seg->start < FLASH_START) { |
178 | fprintf(stderr, "Error: Segment is outside of flash bounds\n"); |
179 | return -1; |
180 | } |
181 | if (seg->start + seg->length > FLASH_END) { |
182 | fprintf(stderr, "Error: Segment is outside of flash bounds\n"); |
183 | return -1; |
184 | } |
185 | if (!can_write_bl && seg->start < BOOTLOADER_END) { |
186 | fprintf(stderr, "Attempted to write bootloader but bootloader writes are not enabled\n"); |
187 | return -1; |
188 | } |
189 | } |
190 | return 0; |
191 | } |
192 | |
193 | // Load an ELF file and prepare it for flashing |
194 | int flash_load(flash_file_t *ctx, const char *name, int can_write_bl) |
195 | { |
196 | FILE *fd = NULL; |
197 | Elf32_Ehdr ehdr; |
198 | Elf32_Phdr *phdrs = NULL; |
199 | int num_phdrs; |
200 | int res; |
201 | |
202 | fd = fopen(name, "rb"); |
203 | if (!fd) { |
204 | fprintf(stderr, "Could not open file '%s': ", name); |
205 | perror(NULL); |
206 | goto fail; |
207 | } |
208 | |
209 | fprintf(stderr, "Loading ELF file '%s'...\n", name); |
210 | |
211 | if (fread(&ehdr, sizeof(ehdr), 1, fd) != 1) { |
212 | fprintf(stderr, "Error while reading ELF file header\n"); |
213 | goto fail; |
214 | } |
215 | if (memcmp(ehdr.e_ident, elf_ident, sizeof(elf_ident)) |
216 | || le32(ehdr.e_version) != 1) |
217 | { |
218 | fprintf(stderr, "Not an ELF file or wrong ELF type\n"); |
219 | goto fail; |
220 | } |
221 | if (le16(ehdr.e_type) != ET_EXEC) { |
222 | fprintf(stderr, "ELF is not executable\n"); |
223 | goto fail; |
224 | } |
225 | if (le16(ehdr.e_machine) != EM_ARM) { |
226 | fprintf(stderr, "Wrong ELF architecture\n"); |
227 | goto fail; |
228 | } |
229 | if (!ehdr.e_phnum || !ehdr.e_phoff) { |
230 | fprintf(stderr, "ELF has no PHDRs\n"); |
231 | goto fail; |
232 | } |
233 | if (le16(ehdr.e_phentsize) != sizeof(Elf32_Phdr)) { |
234 | // could be a structure padding issue... |
235 | fprintf(stderr, "Either the ELF file or this code is made of fail\n"); |
236 | goto fail; |
237 | } |
238 | num_phdrs = le16(ehdr.e_phnum); |
239 | |
240 | phdrs = malloc(le16(ehdr.e_phnum) * sizeof(Elf32_Phdr)); |
241 | if (!phdrs) { |
242 | fprintf(stderr, "Out of memory\n"); |
243 | goto fail; |
244 | } |
245 | if (fseek(fd, le32(ehdr.e_phoff), SEEK_SET) < 0) { |
246 | fprintf(stderr, "Error while reading ELF PHDRs\n"); |
247 | goto fail; |
248 | } |
249 | if (fread(phdrs, sizeof(Elf32_Phdr), num_phdrs, fd) != num_phdrs) { |
250 | fprintf(stderr, "Error while reading ELF PHDRs\n"); |
251 | goto fail; |
252 | } |
253 | |
254 | res = build_segs_from_phdrs(ctx, fd, phdrs, num_phdrs); |
255 | if (res < 0) |
256 | goto fail; |
257 | res = check_segs(ctx, can_write_bl); |
258 | if (res < 0) |
259 | goto fail; |
260 | |
66d6ba70 |
261 | free(phdrs); |
8fe1a992 |
262 | fclose(fd); |
263 | ctx->filename = name; |
264 | return 0; |
265 | |
266 | fail: |
267 | if (phdrs) |
268 | free(phdrs); |
269 | if (fd) |
270 | fclose(fd); |
271 | flash_free(ctx); |
272 | return -1; |
273 | } |
6e4d4ee6 |
274 | |
8fe1a992 |
275 | // Get the state of the proxmark, backwards compatible |
276 | static int get_proxmark_state(uint32_t *state) |
6e4d4ee6 |
277 | { |
28fdb04f |
278 | UsbCommand c; |
8fe1a992 |
279 | c.cmd = CMD_DEVICE_INFO; |
28fdb04f |
280 | SendCommand(&c); |
281 | UsbCommand resp; |
8fe1a992 |
282 | ReceiveCommand(&resp); |
283 | |
284 | // Three outcomes: |
285 | // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK |
286 | // 2. The old os code will respond with CMD_DEBUG_PRINT_STRING and "unknown command" |
287 | // 3. The new bootrom and os codes will respond with CMD_DEVICE_INFO and flags |
288 | |
289 | switch (resp.cmd) { |
290 | case CMD_ACK: |
291 | *state = DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM; |
292 | break; |
293 | case CMD_DEBUG_PRINT_STRING: |
294 | *state = DEVICE_INFO_FLAG_CURRENT_MODE_OS; |
295 | break; |
296 | case CMD_DEVICE_INFO: |
297 | *state = resp.arg[0]; |
298 | break; |
299 | default: |
43534cba |
300 | fprintf(stderr, "Error: Couldn't get proxmark state, bad response type: 0x%04" PRIx64 "\n", resp.cmd); |
8fe1a992 |
301 | return -1; |
302 | break; |
303 | } |
304 | |
305 | return 0; |
6e4d4ee6 |
306 | } |
307 | |
8fe1a992 |
308 | // Enter the bootloader to be able to start flashing |
e12b82d3 |
309 | static int enter_bootloader(char *serial_port_name) |
6e4d4ee6 |
310 | { |
8fe1a992 |
311 | uint32_t state; |
312 | |
313 | if (get_proxmark_state(&state) < 0) |
314 | return -1; |
315 | |
316 | if (state & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) { |
317 | /* Already in flash state, we're done. */ |
318 | return 0; |
319 | } |
320 | |
321 | if (state & DEVICE_INFO_FLAG_CURRENT_MODE_OS) { |
322 | fprintf(stderr,"Entering bootloader...\n"); |
28fdb04f |
323 | UsbCommand c; |
8fe1a992 |
324 | memset(&c, 0, sizeof (c)); |
325 | |
326 | if ((state & DEVICE_INFO_FLAG_BOOTROM_PRESENT) |
327 | && (state & DEVICE_INFO_FLAG_OSIMAGE_PRESENT)) |
328 | { |
329 | // New style handover: Send CMD_START_FLASH, which will reset the board |
330 | // and enter the bootrom on the next boot. |
331 | c.cmd = CMD_START_FLASH; |
28fdb04f |
332 | SendCommand(&c); |
8fe1a992 |
333 | fprintf(stderr,"(Press and release the button only to abort)\n"); |
334 | } else { |
335 | // Old style handover: Ask the user to press the button, then reset the board |
336 | c.cmd = CMD_HARDWARE_RESET; |
28fdb04f |
337 | SendCommand(&c); |
8fe1a992 |
338 | fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); |
339 | } |
d8193fa5 |
340 | msleep(100); |
8fe1a992 |
341 | CloseProxmark(); |
d8193fa5 |
342 | |
e654346b |
343 | fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); |
d8193fa5 |
344 | do { |
8fe1a992 |
345 | sleep(1); |
346 | fprintf(stderr, "."); |
d8193fa5 |
347 | } while (!OpenProxmark(0)); |
8fe1a992 |
348 | fprintf(stderr," Found.\n"); |
349 | |
350 | return 0; |
351 | } |
352 | |
353 | fprintf(stderr, "Error: Unknown Proxmark mode\n"); |
354 | return -1; |
6e4d4ee6 |
355 | } |
356 | |
8fe1a992 |
357 | static int wait_for_ack(void) |
6e4d4ee6 |
358 | { |
28fdb04f |
359 | UsbCommand ack; |
8fe1a992 |
360 | ReceiveCommand(&ack); |
361 | if (ack.cmd != CMD_ACK) { |
43534cba |
362 | printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); |
8fe1a992 |
363 | return -1; |
364 | } |
365 | return 0; |
6e4d4ee6 |
366 | } |
367 | |
8fe1a992 |
368 | // Go into flashing mode |
e12b82d3 |
369 | int flash_start_flashing(int enable_bl_writes,char *serial_port_name) |
6e4d4ee6 |
370 | { |
8fe1a992 |
371 | uint32_t state; |
372 | |
e12b82d3 |
373 | if (enter_bootloader(serial_port_name) < 0) |
8fe1a992 |
374 | return -1; |
375 | |
376 | if (get_proxmark_state(&state) < 0) |
377 | return -1; |
378 | |
379 | if (state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { |
380 | // This command is stupid. Why the heck does it care which area we're |
381 | // flashing, as long as it's not the bootloader area? The mind boggles. |
28fdb04f |
382 | UsbCommand c = {CMD_START_FLASH}; |
8fe1a992 |
383 | |
384 | if (enable_bl_writes) { |
385 | c.arg[0] = FLASH_START; |
386 | c.arg[1] = FLASH_END; |
387 | c.arg[2] = START_FLASH_MAGIC; |
388 | } else { |
389 | c.arg[0] = BOOTLOADER_END; |
390 | c.arg[1] = FLASH_END; |
391 | c.arg[2] = 0; |
392 | } |
28fdb04f |
393 | SendCommand(&c); |
8fe1a992 |
394 | return wait_for_ack(); |
395 | } else { |
396 | fprintf(stderr, "Note: Your bootloader does not understand the new START_FLASH command\n"); |
397 | fprintf(stderr, " It is recommended that you update your bootloader\n\n"); |
398 | } |
399 | |
400 | return 0; |
6e4d4ee6 |
401 | } |
402 | |
8fe1a992 |
403 | static int write_block(uint32_t address, uint8_t *data, uint32_t length) |
6e4d4ee6 |
404 | { |
8fe1a992 |
405 | uint8_t block_buf[BLOCK_SIZE]; |
406 | |
407 | memset(block_buf, 0xFF, BLOCK_SIZE); |
408 | memcpy(block_buf, data, length); |
28fdb04f |
409 | UsbCommand c; |
8fe1a992 |
410 | c.cmd = CMD_FINISH_WRITE; |
411 | c.arg[0] = address; |
28fdb04f |
412 | memcpy(c.d.asBytes, block_buf, length); |
413 | SendCommand(&c); |
414 | return wait_for_ack(); |
6e4d4ee6 |
415 | } |
416 | |
8fe1a992 |
417 | // Write a file's segments to Flash |
418 | int flash_write(flash_file_t *ctx) |
6e4d4ee6 |
419 | { |
8fe1a992 |
420 | fprintf(stderr, "Writing segments for file: %s\n", ctx->filename); |
421 | for (int i = 0; i < ctx->num_segs; i++) { |
422 | flash_seg_t *seg = &ctx->segments[i]; |
423 | |
424 | uint32_t length = seg->length; |
425 | uint32_t blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE; |
426 | uint32_t end = seg->start + length; |
427 | |
428 | fprintf(stderr, " 0x%08x..0x%08x [0x%x / %d blocks]", |
429 | seg->start, end - 1, length, blocks); |
430 | |
431 | int block = 0; |
432 | uint8_t *data = seg->data; |
433 | uint32_t baddr = seg->start; |
434 | |
435 | while (length) { |
436 | uint32_t block_size = length; |
437 | if (block_size > BLOCK_SIZE) |
438 | block_size = BLOCK_SIZE; |
439 | |
440 | if (write_block(baddr, data, block_size) < 0) { |
441 | fprintf(stderr, " ERROR\n"); |
442 | fprintf(stderr, "Error writing block %d of %d\n", block, blocks); |
443 | return -1; |
444 | } |
445 | |
446 | data += block_size; |
447 | baddr += block_size; |
448 | length -= block_size; |
449 | block++; |
450 | fprintf(stderr, "."); |
451 | } |
452 | fprintf(stderr, " OK\n"); |
453 | } |
454 | return 0; |
6e4d4ee6 |
455 | } |
456 | |
8fe1a992 |
457 | // free a file context |
458 | void flash_free(flash_file_t *ctx) |
7fe9b0b7 |
459 | { |
8fe1a992 |
460 | if (!ctx) |
461 | return; |
462 | if (ctx->segments) { |
463 | for (int i = 0; i < ctx->num_segs; i++) |
464 | free(ctx->segments[i].data); |
465 | free(ctx->segments); |
466 | ctx->segments = NULL; |
467 | ctx->num_segs = 0; |
468 | } |
469 | } |
470 | |
471 | // just reset the unit |
472 | int flash_stop_flashing(void) { |
28fdb04f |
473 | UsbCommand c = {CMD_HARDWARE_RESET}; |
28fdb04f |
474 | SendCommand(&c); |
d8193fa5 |
475 | msleep(100); |
28fdb04f |
476 | return 0; |
6e4d4ee6 |
477 | } |