]>
Commit | Line | Data |
---|---|---|
540a2bce SR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2020 Marvell International Ltd. | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <stdint.h> | |
8 | #include <stddef.h> | |
9 | #include <sys/types.h> | |
10 | #include <sys/stat.h> | |
11 | #include <fcntl.h> | |
12 | #include <unistd.h> | |
13 | #include <stdbool.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <getopt.h> | |
17 | #include <arpa/inet.h> | |
18 | #include <linux/compiler.h> | |
19 | #include <u-boot/crc.h> | |
20 | ||
21 | #include "mkimage.h" | |
22 | ||
23 | #include "../arch/mips/mach-octeon/include/mach/cvmx-bootloader.h" | |
24 | ||
25 | #define BUF_SIZE (16 * 1024) | |
26 | #define NAME_LEN 100 | |
27 | ||
28 | /* word offset */ | |
29 | #define WOFFSETOF(type, elem) (offsetof(type, elem) / 4) | |
30 | ||
31 | static int stage2_flag; | |
32 | static int stage_1_5_flag; | |
33 | static int stage_1_flag; | |
34 | ||
35 | /* Getoptions variables must be global */ | |
36 | static int failsafe_flag; | |
37 | static int pciboot_flag; | |
38 | static int env_flag; | |
39 | ||
40 | static const struct option long_options[] = { | |
41 | /* These options set a flag. */ | |
42 | {"failsafe", no_argument, &failsafe_flag, 1}, | |
43 | {"pciboot", no_argument, &pciboot_flag, 1}, | |
44 | {"nandstage2", no_argument, &stage2_flag, 1}, | |
45 | {"spistage2", no_argument, &stage2_flag, 1}, | |
46 | {"norstage2", no_argument, &stage2_flag, 1}, | |
47 | {"stage2", no_argument, &stage2_flag, 1}, | |
48 | {"stage1.5", no_argument, &stage_1_5_flag, 1}, | |
49 | {"stage1", no_argument, &stage_1_flag, 1}, | |
50 | {"environment", no_argument, &env_flag, 1}, | |
51 | /* | |
52 | * These options don't set a flag. | |
53 | * We distinguish them by their indices. | |
54 | */ | |
55 | {"board", required_argument, 0, 0}, | |
56 | {"text_base", required_argument, 0, 0}, | |
57 | {0, 0, 0, 0} | |
58 | }; | |
59 | ||
60 | static int lookup_board_type(char *board_name) | |
61 | { | |
62 | int i; | |
63 | int board_type = 0; | |
64 | char *substr = NULL; | |
65 | ||
66 | /* Detect stage 2 bootloader boards */ | |
67 | if (strcasestr(board_name, "_stage2")) { | |
68 | printf("Stage 2 bootloader detected from substring %s in name %s\n", | |
69 | "_stage2", board_name); | |
70 | stage2_flag = 1; | |
71 | } else { | |
72 | printf("Stage 2 bootloader NOT detected from name \"%s\"\n", | |
73 | board_name); | |
74 | } | |
75 | ||
76 | if (strcasestr(board_name, "_stage1")) { | |
77 | printf("Stage 1 bootloader detected from substring %s in name %s\n", | |
78 | "_stage1", board_name); | |
79 | stage_1_flag = 1; | |
80 | } | |
81 | ||
82 | /* Generic is a special case since there are numerous sub-types */ | |
83 | if (!strncasecmp("generic", board_name, strlen("generic"))) | |
84 | return CVMX_BOARD_TYPE_GENERIC; | |
85 | ||
86 | /* | |
87 | * If we're an eMMC stage 2 bootloader, cut off the _emmc_stage2 | |
88 | * part of the name. | |
89 | */ | |
90 | substr = strcasestr(board_name, "_emmc_stage2"); | |
91 | if (substr && (substr[strlen("_emmc_stage2")] == '\0')) { | |
92 | /*return CVMX_BOARD_TYPE_GENERIC;*/ | |
93 | ||
94 | printf(" Converting board name %s to ", board_name); | |
95 | *substr = '\0'; | |
96 | printf("%s\n", board_name); | |
97 | } | |
98 | ||
99 | /* | |
100 | * If we're a NAND stage 2 bootloader, cut off the _nand_stage2 | |
101 | * part of the name. | |
102 | */ | |
103 | substr = strcasestr(board_name, "_nand_stage2"); | |
104 | if (substr && (substr[strlen("_nand_stage2")] == '\0')) { | |
105 | /*return CVMX_BOARD_TYPE_GENERIC;*/ | |
106 | ||
107 | printf(" Converting board name %s to ", board_name); | |
108 | *substr = '\0'; | |
109 | printf("%s\n", board_name); | |
110 | } | |
111 | ||
112 | /* | |
113 | * If we're a SPI stage 2 bootloader, cut off the _spi_stage2 | |
114 | * part of the name. | |
115 | */ | |
116 | substr = strcasestr(board_name, "_spi_stage2"); | |
117 | if (substr && (substr[strlen("_spi_stage2")] == '\0')) { | |
118 | printf(" Converting board name %s to ", board_name); | |
119 | *substr = '\0'; | |
120 | printf("%s\n", board_name); | |
121 | } | |
122 | ||
123 | for (i = CVMX_BOARD_TYPE_NULL; i < CVMX_BOARD_TYPE_MAX; i++) | |
124 | if (!strcasecmp(cvmx_board_type_to_string(i), board_name)) | |
125 | board_type = i; | |
126 | ||
127 | for (i = CVMX_BOARD_TYPE_CUST_DEFINED_MIN; | |
128 | i < CVMX_BOARD_TYPE_CUST_DEFINED_MAX; i++) | |
129 | if (!strncasecmp(cvmx_board_type_to_string(i), board_name, | |
130 | strlen(cvmx_board_type_to_string(i)))) | |
131 | board_type = i; | |
132 | ||
133 | for (i = CVMX_BOARD_TYPE_CUST_PRIVATE_MIN; | |
134 | i < CVMX_BOARD_TYPE_CUST_PRIVATE_MAX; i++) | |
135 | if (!strncasecmp(cvmx_board_type_to_string(i), board_name, | |
136 | strlen(cvmx_board_type_to_string(i)))) | |
137 | board_type = i; | |
138 | ||
139 | return board_type; | |
140 | } | |
141 | ||
142 | static void usage(void) | |
143 | { | |
144 | printf("Usage: update_octeon_header <filename> <board_name> [--failsafe] [--text_base=0xXXXXX]\n"); | |
145 | } | |
146 | ||
147 | int main(int argc, char *argv[]) | |
148 | { | |
149 | int fd; | |
150 | uint8_t buf[BUF_SIZE]; | |
151 | uint32_t data_crc = 0; | |
152 | int len; | |
153 | int data_len = 0; | |
154 | struct bootloader_header header; | |
155 | char filename[NAME_LEN]; | |
156 | int i; | |
157 | int option_index = 0; /* getopt_long stores the option index here. */ | |
158 | char board_name[NAME_LEN] = { 0 }; | |
159 | char tmp_board_name[NAME_LEN] = { 0 }; | |
160 | int c; | |
161 | int board_type = 0; | |
162 | unsigned long long address = 0; | |
163 | ssize_t ret; | |
164 | const char *type_str = NULL; | |
165 | int hdr_size = sizeof(struct bootloader_header); | |
166 | ||
167 | /* | |
168 | * Compile time check, if the size of the bootloader_header structure | |
169 | * has changed. | |
170 | */ | |
171 | compiletime_assert(sizeof(struct bootloader_header) == 192, | |
172 | "Octeon bootloader header size changed (!= 192)!"); | |
173 | ||
174 | /* Bail out, if argument count is incorrect */ | |
175 | if (argc < 3) { | |
176 | usage(); | |
177 | return -1; | |
178 | } | |
179 | ||
180 | debug("header size is: %d bytes\n", hdr_size); | |
181 | ||
182 | /* Parse command line options using getopt_long */ | |
183 | while (1) { | |
184 | c = getopt_long(argc, argv, "h", long_options, &option_index); | |
185 | ||
186 | /* Detect the end of the options. */ | |
187 | if (c == -1) | |
188 | break; | |
189 | ||
190 | switch (c) { | |
191 | /* All long options handled in case 0 */ | |
192 | case 0: | |
193 | /* If this option set a flag, do nothing else now. */ | |
194 | if (long_options[option_index].flag != 0) | |
195 | break; | |
196 | debug("option(l) %s", long_options[option_index].name); | |
197 | ||
198 | if (!optarg) { | |
199 | usage(); | |
200 | return -1; | |
201 | } | |
202 | debug(" with arg %s\n", optarg); | |
203 | ||
204 | if (!strcmp(long_options[option_index].name, "board")) { | |
205 | if (strlen(optarg) >= NAME_LEN) { | |
206 | printf("strncpy() issue detected!"); | |
207 | exit(-1); | |
208 | } | |
209 | strncpy(board_name, optarg, NAME_LEN); | |
210 | ||
211 | printf("Using user supplied board name: %s\n", | |
212 | board_name); | |
213 | } else if (!strcmp(long_options[option_index].name, | |
214 | "text_base")) { | |
215 | address = strtoull(optarg, NULL, 0); | |
216 | printf("Address of image is: 0x%llx\n", | |
217 | (unsigned long long)address); | |
218 | if (!(address & 0xFFFFFFFFULL << 32)) { | |
219 | if (address & 1 << 31) { | |
220 | address |= 0xFFFFFFFFULL << 32; | |
221 | printf("Converting address to 64 bit compatibility space: 0x%llx\n", | |
222 | address); | |
223 | } | |
224 | } | |
225 | } | |
226 | break; | |
227 | ||
228 | case 'h': | |
229 | case '?': | |
230 | /* getopt_long already printed an error message. */ | |
231 | usage(); | |
232 | return -1; | |
233 | ||
234 | default: | |
235 | abort(); | |
236 | } | |
237 | } | |
238 | ||
239 | if (optind < argc) { | |
240 | /* | |
241 | * We only support one argument - an optional bootloader | |
242 | * file name | |
243 | */ | |
244 | if (argc - optind > 2) { | |
245 | fprintf(stderr, "non-option ARGV-elements: "); | |
246 | while (optind < argc) | |
247 | fprintf(stderr, "%s ", argv[optind++]); | |
248 | fprintf(stderr, "\n"); | |
249 | ||
250 | usage(); | |
251 | return -1; | |
252 | } | |
253 | } | |
254 | ||
255 | if (strlen(argv[optind]) >= NAME_LEN) { | |
256 | fprintf(stderr, "strncpy() issue detected!"); | |
257 | exit(-1); | |
258 | } | |
259 | strncpy(filename, argv[optind], NAME_LEN); | |
260 | ||
261 | if (board_name[0] == '\0') { | |
262 | if (strlen(argv[optind + 1]) >= NAME_LEN) { | |
263 | fprintf(stderr, "strncpy() issue detected!"); | |
264 | exit(-1); | |
265 | } | |
266 | strncpy(board_name, argv[optind + 1], NAME_LEN); | |
267 | } | |
268 | ||
269 | if (strlen(board_name) >= NAME_LEN) { | |
270 | fprintf(stderr, "strncpy() issue detected!"); | |
271 | exit(-1); | |
272 | } | |
273 | strncpy(tmp_board_name, board_name, NAME_LEN); | |
274 | ||
275 | fd = open(filename, O_RDWR); | |
276 | if (fd < 0) { | |
277 | fprintf(stderr, "Unable to open file: %s\n", filename); | |
278 | exit(-1); | |
279 | } | |
280 | ||
281 | if (failsafe_flag) | |
282 | printf("Setting failsafe flag\n"); | |
283 | ||
284 | if (strlen(board_name)) { | |
285 | int offset = 0; | |
286 | ||
287 | printf("Supplied board name of: %s\n", board_name); | |
288 | ||
289 | if (strstr(board_name, "failsafe")) { | |
290 | failsafe_flag = 1; | |
291 | printf("Setting failsafe flag based on board name\n"); | |
292 | } | |
293 | /* Skip leading octeon_ if present. */ | |
294 | if (!strncmp(board_name, "octeon_", 7)) | |
295 | offset = 7; | |
296 | ||
297 | /* | |
298 | * Check to see if 'failsafe' is in the name. If so, set the | |
299 | * failsafe flag. Also, ignore extra trailing characters on | |
300 | * passed parameter when comparing against board names. | |
301 | * We actually use the configuration name from u-boot, so it | |
302 | * may have some other variant names. Variants other than | |
303 | * failsafe _must_ be passed to this program explicitly | |
304 | */ | |
305 | ||
306 | board_type = lookup_board_type(board_name + offset); | |
307 | if (!board_type) { | |
308 | /* Retry with 'cust_' prefix to catch boards that are | |
309 | * in the customer section (such as nb5) | |
310 | */ | |
311 | sprintf(tmp_board_name, "cust_%s", board_name + offset); | |
312 | board_type = lookup_board_type(tmp_board_name); | |
313 | } | |
314 | ||
315 | /* reset to original value */ | |
316 | strncpy(tmp_board_name, board_name, NAME_LEN); | |
317 | if (!board_type) { | |
318 | /* | |
319 | * Retry with 'cust_private_' prefix to catch boards | |
320 | * that are in the customer private section | |
321 | */ | |
322 | sprintf(tmp_board_name, "cust_private_%s", | |
323 | board_name + offset); | |
324 | board_type = lookup_board_type(tmp_board_name); | |
325 | } | |
326 | ||
327 | if (!board_type) { | |
328 | fprintf(stderr, | |
329 | "ERROR: unable to determine board type\n"); | |
330 | exit(-1); | |
331 | } | |
332 | printf("Board type is: %d: %s\n", board_type, | |
333 | cvmx_board_type_to_string(board_type)); | |
334 | } else { | |
335 | fprintf(stderr, "Board name must be specified!\n"); | |
336 | exit(-1); | |
337 | } | |
338 | ||
339 | /* | |
340 | * Check to see if there is either an existing header, or that there | |
341 | * are zero valued bytes where we want to put the header | |
342 | */ | |
343 | len = read(fd, buf, BUF_SIZE); | |
344 | if (len > 0) { | |
345 | /* | |
346 | * Copy the header, as the first word (jump instruction, needs | |
347 | * to remain the same. | |
348 | */ | |
349 | memcpy(&header, buf, hdr_size); | |
350 | /* | |
351 | * Check to see if we have zero bytes (excluding first 4, which | |
352 | * are the jump instruction) | |
353 | */ | |
354 | for (i = 1; i < hdr_size / 4; i++) { | |
355 | if (((uint32_t *)buf)[i]) { | |
356 | fprintf(stderr, | |
357 | "ERROR: non-zero word found %x in location %d required for header, aborting\n", | |
358 | ((uint32_t *)buf)[i], i); | |
359 | exit(-1); | |
360 | } | |
361 | } | |
362 | printf("Zero bytes found in header location, adding header.\n"); | |
363 | ||
364 | } else { | |
365 | fprintf(stderr, "Unable to read from file %s\n", filename); | |
366 | exit(-1); | |
367 | } | |
368 | ||
369 | /* Read data bytes and generate CRC */ | |
370 | lseek(fd, hdr_size, SEEK_SET); | |
371 | ||
372 | while ((len = read(fd, buf, BUF_SIZE)) > 0) { | |
373 | data_crc = crc32(data_crc, buf, len); | |
374 | data_len += len; | |
375 | } | |
376 | printf("CRC of data: 0x%x, length: %d\n", data_crc, data_len); | |
377 | ||
378 | /* Now create the new header */ | |
379 | header.magic = htonl(BOOTLOADER_HEADER_MAGIC); | |
380 | header.maj_rev = htons(BOOTLOADER_HEADER_CURRENT_MAJOR_REV); | |
381 | header.min_rev = htons(BOOTLOADER_HEADER_CURRENT_MINOR_REV); | |
382 | header.dlen = htonl(data_len); | |
383 | header.dcrc = htonl(data_crc); | |
384 | header.board_type = htons(board_type); | |
385 | header.address = address; | |
386 | if (failsafe_flag) | |
387 | header.flags |= htonl(BL_HEADER_FLAG_FAILSAFE); | |
388 | ||
389 | printf("Stage 2 flag is %sset\n", stage2_flag ? "" : "not "); | |
390 | printf("Stage 1 flag is %sset\n", stage_1_flag ? "" : "not "); | |
391 | if (pciboot_flag) | |
392 | header.image_type = htons(BL_HEADER_IMAGE_PCIBOOT); | |
393 | else if (stage2_flag) | |
394 | header.image_type = htons(BL_HEADER_IMAGE_STAGE2); | |
395 | else if (stage_1_flag) | |
396 | header.image_type = htons(BL_HEADER_IMAGE_STAGE1); | |
397 | else if (env_flag) | |
398 | header.image_type = htons(BL_HEADER_IMAGE_UBOOT_ENV); | |
399 | else if (stage_1_5_flag || stage_1_flag) | |
400 | header.image_type = htons(BL_HEADER_IMAGE_PRE_UBOOT); | |
401 | else | |
402 | header.image_type = htons(BL_HEADER_IMAGE_NOR); | |
403 | ||
404 | switch (ntohs(header.image_type)) { | |
405 | case BL_HEADER_IMAGE_UNKNOWN: | |
406 | type_str = "Unknown"; | |
407 | break; | |
408 | case BL_HEADER_IMAGE_STAGE1: | |
409 | type_str = "Stage 1"; | |
410 | break; | |
411 | case BL_HEADER_IMAGE_STAGE2: | |
412 | type_str = "Stage 2"; | |
413 | break; | |
414 | case BL_HEADER_IMAGE_PRE_UBOOT: | |
415 | type_str = "Pre-U-Boot"; | |
416 | break; | |
417 | case BL_HEADER_IMAGE_STAGE3: | |
418 | type_str = "Stage 3"; | |
419 | break; | |
420 | case BL_HEADER_IMAGE_NOR: | |
421 | type_str = "NOR"; | |
422 | break; | |
423 | case BL_HEADER_IMAGE_PCIBOOT: | |
424 | type_str = "PCI Boot"; | |
425 | break; | |
426 | case BL_HEADER_IMAGE_UBOOT_ENV: | |
427 | type_str = "U-Boot Environment"; | |
428 | break; | |
429 | default: | |
430 | if (ntohs(header.image_type) >= BL_HEADER_IMAGE_CUST_RESERVED_MIN && | |
431 | ntohs(header.image_type) <= BL_HEADER_IMAGE_CUST_RESERVED_MAX) | |
432 | type_str = "Customer Reserved"; | |
433 | else | |
434 | type_str = "Unsupported"; | |
435 | } | |
436 | printf("Header image type: %s\n", type_str); | |
437 | header.hlen = htons(hdr_size); | |
438 | ||
439 | /* Now compute header CRC over all of the header excluding the CRC */ | |
440 | header.hcrc = crc32(0, (void *)&header, 12); | |
441 | header.hcrc = htonl(crc32(header.hcrc, ((void *)&(header)) + 16, | |
442 | hdr_size - 16)); | |
443 | ||
444 | /* Seek to beginning of file */ | |
445 | lseek(fd, 0, SEEK_SET); | |
446 | ||
447 | /* Write header to file */ | |
448 | ret = write(fd, &header, hdr_size); | |
449 | if (ret < 0) | |
450 | perror("write"); | |
451 | ||
452 | close(fd); | |
453 | ||
454 | printf("Header CRC: 0x%x\n", ntohl(header.hcrc)); | |
455 | return 0; | |
456 | } |