]>
Commit | Line | Data |
---|---|---|
6658905f | 1 | #include <usb.h> |
2 | #include <stdio.h> | |
3 | #include <unistd.h> | |
4 | #include <stdlib.h> | |
a146075d | 5 | #include <stdint.h> |
6 | #include <stdbool.h> | |
6658905f | 7 | #include <strings.h> |
8 | #include <string.h> | |
9 | #include <errno.h> | |
10 | #include <ctype.h> | |
11 | ||
cd00aa30 | 12 | #include "prox.h" |
6658905f | 13 | #include "proxmark3.h" |
14 | ||
a146075d | 15 | static uint32_t ExpectedAddr; |
16 | static uint8_t QueuedToSend[256]; | |
17 | static bool AllWritten; | |
a5b1ba20 | 18 | #define PHYSICAL_FLASH_START 0x100000 |
19 | ||
20 | struct partition { | |
21 | int start; | |
22 | int end; | |
23 | int precious; | |
24 | const char *name; | |
25 | }; | |
26 | struct partition partitions[] = { | |
27 | {0x100000, 0x102000, 1, "bootrom"}, | |
28 | {0x102000, 0x110000, 0, "fpga"}, | |
29 | {0x110000, 0x140000, 0, "os"}, | |
30 | }; | |
31 | ||
32 | /* If translate is set, subtract PHYSICAL_FLASH_START to translate for old | |
33 | * bootroms. | |
34 | */ | |
35 | static void FlushPrevious(int translate) | |
6658905f | 36 | { |
37 | UsbCommand c; | |
38 | memset(&c, 0, sizeof(c)); | |
39 | ||
40 | printf("expected = %08x flush, ", ExpectedAddr); | |
41 | ||
42 | int i; | |
43 | for(i = 0; i < 240; i += 48) { | |
44 | c.cmd = CMD_SETUP_WRITE; | |
45 | memcpy(c.d.asBytes, QueuedToSend+i, 48); | |
3a8464f0 | 46 | c.arg[0] = (i/4); |
a146075d | 47 | SendCommand(&c, true); |
6658905f | 48 | } |
49 | ||
50 | c.cmd = CMD_FINISH_WRITE; | |
3a8464f0 | 51 | c.arg[0] = (ExpectedAddr-1) & (~255); |
a5b1ba20 | 52 | if(translate) { |
3a8464f0 | 53 | c.arg[0] -= PHYSICAL_FLASH_START; |
a5b1ba20 | 54 | } |
3a8464f0 | 55 | printf("c.arg[0] = %08x\r", c.arg[0]); |
6658905f | 56 | memcpy(c.d.asBytes, QueuedToSend+240, 16); |
a146075d | 57 | SendCommand(&c, true); |
6658905f | 58 | |
a146075d | 59 | AllWritten = true; |
6658905f | 60 | } |
61 | ||
a5b1ba20 | 62 | /* Where must be between start_addr (inclusive) and end_addr (exclusive). |
63 | */ | |
a146075d | 64 | static void GotByte(uint32_t where, uint8_t which, int start_addr, int end_addr, int translate) |
6658905f | 65 | { |
a146075d | 66 | AllWritten = false; |
a5b1ba20 | 67 | |
68 | if(where < start_addr || where >= end_addr) { | |
69 | printf("bad: got byte at %08x, outside of range %08x-%08x\n", where, start_addr, end_addr); | |
70 | exit(-1); | |
71 | } | |
6658905f | 72 | |
73 | if(where != ExpectedAddr) { | |
74 | printf("bad: got at %08x, expected at %08x\n", where, ExpectedAddr); | |
75 | exit(-1); | |
76 | } | |
77 | QueuedToSend[where & 255] = which; | |
78 | ExpectedAddr++; | |
79 | ||
80 | if((where & 255) == 255) { | |
81 | // we have completed a full page | |
a5b1ba20 | 82 | FlushPrevious(translate); |
6658905f | 83 | } |
84 | } | |
85 | ||
86 | static int HexVal(int c) | |
87 | { | |
88 | c = tolower(c); | |
89 | if(c >= '0' && c <= '9') { | |
90 | return c - '0'; | |
91 | } else if(c >= 'a' && c <= 'f') { | |
92 | return (c - 'a') + 10; | |
93 | } else { | |
94 | printf("bad hex digit '%c'\n", c); | |
95 | exit(-1); | |
96 | } | |
97 | } | |
98 | ||
a146075d | 99 | static uint8_t HexByte(char *s) |
6658905f | 100 | { |
101 | return (HexVal(s[0]) << 4) | HexVal(s[1]); | |
102 | } | |
103 | ||
a5b1ba20 | 104 | static void LoadFlashFromSRecords(const char *file, int start_addr, int end_addr, int translate) |
6658905f | 105 | { |
a5b1ba20 | 106 | ExpectedAddr = start_addr; |
107 | ||
6658905f | 108 | FILE *f = fopen(file, "r"); |
109 | if(!f) { | |
110 | printf("couldn't open file\n"); | |
111 | exit(-1); | |
112 | } | |
113 | ||
114 | char line[512]; | |
115 | while(fgets(line, sizeof(line), f)) { | |
116 | if(memcmp(line, "S3", 2)==0) { | |
117 | char *s = line + 2; | |
118 | int len = HexByte(s) - 5; | |
119 | s += 2; | |
120 | ||
121 | char addrStr[9]; | |
122 | memcpy(addrStr, s, 8); | |
123 | addrStr[8] = '\0'; | |
a146075d | 124 | uint32_t addr; |
6658905f | 125 | sscanf(addrStr, "%x", &addr); |
126 | s += 8; | |
a5b1ba20 | 127 | |
128 | /* Accept files that are located at PHYSICAL_FLASH_START, and files that are located at 0 */ | |
129 | if(addr < PHYSICAL_FLASH_START) | |
130 | addr += PHYSICAL_FLASH_START; | |
6658905f | 131 | |
132 | int i; | |
133 | for(i = 0; i < len; i++) { | |
134 | while((addr+i) > ExpectedAddr) { | |
a5b1ba20 | 135 | GotByte(ExpectedAddr, 0xff, start_addr, end_addr, translate); |
6658905f | 136 | } |
a5b1ba20 | 137 | GotByte(addr+i, HexByte(s), start_addr, end_addr, translate); |
6658905f | 138 | s += 2; |
139 | } | |
140 | } | |
141 | } | |
142 | ||
a5b1ba20 | 143 | if(!AllWritten) FlushPrevious(translate); |
6658905f | 144 | |
145 | fclose(f); | |
146 | printf("\ndone.\n"); | |
147 | } | |
148 | ||
a5b1ba20 | 149 | static int PrepareFlash(struct partition *p, const char *filename, unsigned int state) |
150 | { | |
151 | int translate = 0; | |
152 | if(state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { | |
153 | UsbCommand c; | |
154 | c.cmd = CMD_START_FLASH; | |
3a8464f0 | 155 | c.arg[0] = p->start; |
156 | c.arg[1] = p->end; | |
a5b1ba20 | 157 | |
158 | /* Only send magic when flashing bootrom */ | |
159 | if(p->precious) { | |
3a8464f0 | 160 | c.arg[2] = START_FLASH_MAGIC; |
a5b1ba20 | 161 | } else { |
3a8464f0 | 162 | c.arg[2] = 0; |
a5b1ba20 | 163 | } |
a146075d | 164 | SendCommand(&c, true); |
a5b1ba20 | 165 | translate = 0; |
6658905f | 166 | } else { |
a5b1ba20 | 167 | fprintf(stderr, "Warning: Your bootloader does not understand the new START_FLASH command\n"); |
168 | fprintf(stderr, " It is recommended that you update your bootloader\n\n"); | |
169 | translate = 1; | |
6658905f | 170 | } |
a5b1ba20 | 171 | |
172 | LoadFlashFromSRecords(filename, p->start, p->end, translate); | |
173 | return 1; | |
174 | } | |
6658905f | 175 | |
a5b1ba20 | 176 | static unsigned int GetProxmarkState(void) |
177 | { | |
178 | unsigned int state = 0; | |
179 | ||
180 | UsbCommand c; | |
181 | c.cmd = CMD_DEVICE_INFO; | |
a146075d | 182 | SendCommand(&c, false); |
a5b1ba20 | 183 | |
184 | UsbCommand resp; | |
185 | ReceiveCommand(&resp); | |
186 | /* Three cases: | |
187 | * 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK | |
188 | * 2. The old os code will respond with CMD_DEBUG_PRINT_STRING and "unknown command" | |
189 | * 3. The new bootrom and os codes will respond with CMD_DEVICE_INFO and flags | |
190 | */ | |
191 | ||
192 | switch(resp.cmd) { | |
193 | case CMD_ACK: | |
194 | state = DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM; | |
195 | break; | |
196 | case CMD_DEBUG_PRINT_STRING: | |
197 | state = DEVICE_INFO_FLAG_CURRENT_MODE_OS; | |
198 | break; | |
199 | case CMD_DEVICE_INFO: | |
3a8464f0 | 200 | state = resp.arg[0]; |
a5b1ba20 | 201 | break; |
202 | default: | |
203 | fprintf(stderr, "Couldn't get proxmark state, bad response type: 0x%04X\n", resp.cmd); | |
204 | exit(-1); | |
205 | break; | |
431ae7e0 | 206 | } |
a5b1ba20 | 207 | |
208 | #if 0 | |
209 | if(state & DEVICE_INFO_FLAG_BOOTROM_PRESENT) printf("New bootrom present\n"); | |
210 | if(state & DEVICE_INFO_FLAG_OSIMAGE_PRESENT) printf("New osimage present\n"); | |
211 | if(state & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) printf("Currently in bootrom\n"); | |
212 | if(state & DEVICE_INFO_FLAG_CURRENT_MODE_OS) printf("Currently in OS\n"); | |
213 | #endif | |
214 | ||
215 | return state; | |
216 | } | |
431ae7e0 | 217 | |
a5b1ba20 | 218 | static unsigned int EnterFlashState(void) |
219 | { | |
220 | unsigned int state = GetProxmarkState(); | |
221 | ||
222 | if(state & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) { | |
223 | /* Already in flash state, we're done. */ | |
224 | return state; | |
225 | } | |
226 | ||
227 | if(state & DEVICE_INFO_FLAG_CURRENT_MODE_OS) { | |
431ae7e0 | 228 | fprintf(stderr,"Entering flash-mode...\n"); |
a5b1ba20 | 229 | UsbCommand c; |
431ae7e0 | 230 | bzero(&c, sizeof(c)); |
a5b1ba20 | 231 | |
232 | if( (state & DEVICE_INFO_FLAG_BOOTROM_PRESENT) && (state & DEVICE_INFO_FLAG_OSIMAGE_PRESENT) ) { | |
233 | /* New style handover: Send CMD_START_FLASH, which will reset the board and | |
234 | * enter the bootrom on the next boot. | |
235 | */ | |
236 | c.cmd = CMD_START_FLASH; | |
a146075d | 237 | SendCommand(&c, false); |
a5b1ba20 | 238 | fprintf(stderr,"(You don't have to do anything. Press and release the button only if you want to abort)\n"); |
239 | fprintf(stderr,"Waiting for Proxmark to reappear on USB... "); | |
240 | } else { | |
241 | /* Old style handover: Ask the user to press the button, then reset the board */ | |
242 | c.cmd = CMD_HARDWARE_RESET; | |
a146075d | 243 | SendCommand(&c, false); |
a5b1ba20 | 244 | fprintf(stderr,"(Press and hold down button NOW if your bootloader requires it)\n"); |
245 | fprintf(stderr,"Waiting for Proxmark to reappear on USB... "); | |
246 | } | |
247 | ||
431ae7e0 | 248 | CloseProxmark(); |
249 | sleep(1); | |
250 | ||
431ae7e0 | 251 | while(!(devh=OpenProxmark(0))) { sleep(1); } |
a5b1ba20 | 252 | fprintf(stderr,"Found.\n"); |
253 | ||
254 | return GetProxmarkState(); | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | static void usage(char **argv) | |
261 | { | |
262 | int i; | |
263 | fprintf(stderr, "Usage: %s areas image [image [image]]\n", argv[0]); | |
264 | fprintf(stderr, " areas is a comma-separated list of areas to flash, with no spaces\n"); | |
265 | fprintf(stderr, " Known areas are:"); | |
266 | for(i=0; i<(sizeof(partitions)/sizeof(partitions[0])); i++) { | |
267 | fprintf(stderr, " %s", partitions[i].name); | |
431ae7e0 | 268 | } |
a5b1ba20 | 269 | fprintf(stderr, "\n"); |
270 | fprintf(stderr, " image is the path to the corresponding image\n\n"); | |
271 | fprintf(stderr, "Example: %s os,fpga path/to/osimage.s19 path/to/fpgaimage.s19\n", argv[0]); | |
272 | } | |
6658905f | 273 | |
a5b1ba20 | 274 | /* On first call, have *offset = -1, *length = 0; */ |
275 | static int find_next_area(const char *str, int *offset, int *length) | |
276 | { | |
277 | if(*str == '\0') return 0; | |
278 | if((*offset >= 0) && str[*offset + *length] == '\0') return 0; | |
279 | *offset += 1 + *length; | |
280 | ||
281 | char *next_comma = strchr(str + *offset, ','); | |
282 | if(next_comma == NULL) { | |
283 | *length = strlen(str) - *offset; | |
284 | } else { | |
285 | *length = next_comma-(str+*offset); | |
431ae7e0 | 286 | } |
a5b1ba20 | 287 | return 1; |
288 | } | |
6658905f | 289 | |
a5b1ba20 | 290 | int main(int argc, char **argv) { |
291 | if(argc < 2) { | |
292 | usage(argv); | |
293 | exit(-1); | |
294 | } | |
295 | ||
296 | /* Count area arguments */ | |
297 | int areas = 0, offset=-1, length=0; | |
298 | while(find_next_area(argv[1], &offset, &length)) areas++; | |
299 | ||
300 | if(areas != argc - 2) { | |
301 | usage(argv); | |
302 | exit(-1); | |
303 | } | |
304 | ||
305 | usb_init(); | |
306 | ||
307 | fprintf(stderr,"Waiting for Proxmark to appear on USB... "); | |
308 | while(!(devh=OpenProxmark(0))) { sleep(1); } | |
309 | fprintf(stderr,"Found.\n"); | |
310 | ||
311 | unsigned int state = EnterFlashState(); | |
312 | ||
313 | if( !(state & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) ) { | |
314 | fprintf(stderr, "Proxmark would not enter flash state, abort\n"); | |
315 | exit(-1); | |
316 | } | |
317 | ||
318 | offset=-1; length=0; | |
319 | int current_area = 0; | |
320 | while(find_next_area(argv[1], &offset, &length)) { | |
321 | int i; | |
322 | struct partition *p = NULL; | |
323 | for(i=0; i<sizeof(partitions)/sizeof(partitions[0]); i++) { | |
324 | if(strncmp(partitions[i].name, argv[1] + offset, length) == 0) { | |
325 | /* Check if the name matches the bootrom partition, and if so, require "bootrom" to | |
326 | * be written in full. The other names may be abbreviated. | |
327 | */ | |
328 | if(!partitions[i].precious || (strlen(partitions[i].name) == length)) { | |
329 | p = &partitions[i]; | |
330 | } | |
331 | break; | |
332 | } | |
333 | } | |
334 | ||
335 | if(p == NULL) { | |
336 | fprintf(stderr, "Warning: area name '"); | |
337 | fwrite(argv[1]+offset, length, 1, stderr); | |
338 | fprintf(stderr, "' unknown, ignored\n"); | |
339 | } else { | |
340 | fprintf(stderr, "Flashing %s from %s\n", p->name, argv[2+current_area]); | |
341 | PrepareFlash(p, argv[2+current_area], state); | |
342 | } | |
343 | current_area++; | |
344 | } | |
345 | ||
346 | UsbCommand c; | |
6658905f | 347 | bzero(&c, sizeof(c)); |
348 | c.cmd = CMD_HARDWARE_RESET; | |
a146075d | 349 | SendCommand(&c, false); |
6658905f | 350 | |
351 | CloseProxmark(); | |
352 | ||
353 | fprintf(stderr,"Have a nice day!\n"); | |
354 | ||
355 | return 0; | |
356 | } |