1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2007-2011
4 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
5 * Tom Cubie <tangliang@allwinnertech.com>
6 *
7 * a simple tool to generate bootable image for sunxi platform.
8 */
9#include <fcntl.h>
10#include <stdio.h>
11#include <unistd.h>
12#include <stdlib.h>
13#include <string.h>
14#include <errno.h>
15#include <sunxi_image.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include "imagetool.h"
19
20#define STAMP_VALUE                     0x5F0A6C39
21
22/* check sum functon from sun4i boot code */
23int gen_check_sum(struct boot_file_head *head_p)
24{
25	uint32_t length;
26	uint32_t *buf;
27	uint32_t loop;
28	uint32_t i;
29	uint32_t sum;
30
31	length = le32_to_cpu(head_p->length);
32	if ((length & 0x3) != 0)	/* must 4-byte-aligned */
33		return -1;
34	buf = (uint32_t *)head_p;
35	head_p->check_sum = cpu_to_le32(STAMP_VALUE);	/* fill stamp */
36	loop = length >> 2;
37
38	/* calculate the sum */
39	for (i = 0, sum = 0; i < loop; i++)
40		sum += le32_to_cpu(buf[i]);
41
42	/* write back check sum */
43	head_p->check_sum = cpu_to_le32(sum);
44
45	return 0;
46}
47
48#define SUNXI_SRAM_SIZE 0x8000	/* SoC with smaller size are limited before */
49#define SRAM_LOAD_MAX_SIZE (SUNXI_SRAM_SIZE - sizeof(struct boot_file_head))
50
51/*
52 * BROM (at least on A10 and A20) requires NAND-images to be explicitly aligned
53 * to a multiple of 8K, and rejects the image otherwise. MMC-images are fine
54 * with 512B blocks. To cater for both, align to the largest of the two.
55 */
56#define BLOCK_SIZE 0x2000
57
58struct boot_img {
59	struct boot_file_head header;
60	char code[SRAM_LOAD_MAX_SIZE];
61	char pad[BLOCK_SIZE];
62};
63
64int main(int argc, char *argv[])
65{
66	int fd_in, fd_out;
67	struct boot_img img;
68	unsigned file_size;
69	int count;
70	char *tool_name = argv[0];
71	char *default_dt = NULL;
72
73	/* a sanity check */
74	if ((sizeof(img.header) % 32) != 0) {
75		fprintf(stderr, "ERROR: the SPL header must be a multiple ");
76		fprintf(stderr, "of 32 bytes.\n");
77		return EXIT_FAILURE;
78	}
79
80	/* process optional command line switches */
81	while (argc >= 2 && argv[1][0] == '-') {
82		if (strcmp(argv[1], "--default-dt") == 0) {
83			if (argc >= 3) {
84				default_dt = argv[2];
85				argv += 2;
86				argc -= 2;
87				continue;
88			}
89			fprintf(stderr, "ERROR: no --default-dt arg\n");
90			return EXIT_FAILURE;
91		} else {
92			fprintf(stderr, "ERROR: bad option '%s'\n", argv[1]);
93			return EXIT_FAILURE;
94		}
95	}
96
97	if (argc < 3) {
98		printf("This program converts an input binary file to a sunxi bootable image.\n");
99		printf("\nUsage: %s [options] input_file output_file\n",
100		       tool_name);
101		printf("Where [options] may be:\n");
102		printf("  --default-dt arg         - 'arg' is the default device tree name\n");
103		printf("                             (CONFIG_DEFAULT_DEVICE_TREE).\n");
104		return EXIT_FAILURE;
105	}
106
107	fd_in = open(argv[1], O_RDONLY);
108	if (fd_in < 0) {
109		perror("Open input file");
110		return EXIT_FAILURE;
111	}
112
113	memset(&img, 0, sizeof(img));
114
115	/* get input file size */
116	file_size = lseek(fd_in, 0, SEEK_END);
117
118	if (file_size > SRAM_LOAD_MAX_SIZE) {
119		fprintf(stderr, "ERROR: File too large!\n");
120		return EXIT_FAILURE;
121	}
122
123	fd_out = open(argv[2], O_WRONLY | O_CREAT, 0666);
124	if (fd_out < 0) {
125		perror("Open output file");
126		return EXIT_FAILURE;
127	}
128
129	/* read file to buffer to calculate checksum */
130	lseek(fd_in, 0, SEEK_SET);
131	count = read(fd_in, img.code, file_size);
132	if (count != file_size) {
133		perror("Reading input image");
134		return EXIT_FAILURE;
135	}
136
137	/* fill the header */
138	img.header.b_instruction =	/* b instruction */
139		0xEA000000 |	/* jump to the first instr after the header */
140		((sizeof(struct boot_file_head) / sizeof(int) - 2)
141		 & 0x00FFFFFF);
142	memcpy(img.header.magic, BOOT0_MAGIC, 8);	/* no '0' termination */
143	img.header.length =
144		ALIGN(file_size + sizeof(struct boot_file_head), BLOCK_SIZE);
145	img.header.b_instruction = cpu_to_le32(img.header.b_instruction);
146	img.header.length = cpu_to_le32(img.header.length);
147
148	memcpy(img.header.spl_signature, SPL_SIGNATURE, 3); /* "sunxi" marker */
149	img.header.spl_signature[3] = SPL_HEADER_VERSION;
150
151	if (default_dt) {
152		if (strlen(default_dt) + 1 <= sizeof(img.header.string_pool)) {
153			strcpy((char *)img.header.string_pool, default_dt);
154			img.header.dt_name_offset =
155				cpu_to_le32(offsetof(struct boot_file_head,
156						     string_pool));
157		} else {
158			printf("WARNING: The SPL header is too small\n");
159			printf("         and has no space to store the dt name.\n");
160		}
161	}
162
163	gen_check_sum(&img.header);
164
165	count = write(fd_out, &img, le32_to_cpu(img.header.length));
166	if (count != le32_to_cpu(img.header.length)) {
167		perror("Writing output");
168		return EXIT_FAILURE;
169	}
170
171	close(fd_in);
172	close(fd_out);
173
174	return EXIT_SUCCESS;
175}
176