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 
lookup_board_type(char * board_name)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 
usage(void)142 static void usage(void)
143 {
144 	printf("Usage: update_octeon_header <filename> <board_name> [--failsafe] [--text_base=0xXXXXX]\n");
145 }
146 
main(int argc,char * argv[])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 }
457