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
31static int stage2_flag;
32static int stage_1_5_flag;
33static int stage_1_flag;
34
35/* Getoptions variables must be global */
36static int failsafe_flag;
37static int pciboot_flag;
38static int env_flag;
39
40static 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
60static 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
142static void usage(void)
143{
144	printf("Usage: update_octeon_header <filename> <board_name> [--failsafe] [--text_base=0xXXXXX]\n");
145}
146
147int 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