xref: /openbmc/u-boot/tools/mkexynosspl.c (revision 522e4fbb)
1  /*
2   * Copyright (C) 2012 Samsung Electronics
3   *
4   * SPDX-License-Identifier:	GPL-2.0+
5   */
6  
7  #include <stdio.h>
8  #include <stdlib.h>
9  #include <unistd.h>
10  #include <fcntl.h>
11  #include <errno.h>
12  #include <string.h>
13  #include <sys/stat.h>
14  #include <compiler.h>
15  
16  #define CHECKSUM_OFFSET		(14*1024-4)
17  #define FILE_PERM		(S_IRUSR | S_IWUSR | S_IRGRP \
18  				| S_IWGRP | S_IROTH | S_IWOTH)
19  /*
20   * Requirement for the fixed size SPL header:
21   * IROM code reads first (CHECKSUM_OFFSET + 4) bytes from boot device. It then
22   * calculates the checksum of CHECKSUM_OFFSET bytes and compares with data at
23   * CHECKSUM_OFFSET location.
24   *
25   * Requirement for the variable size SPL header:
26  
27   * IROM code reads the below header to find out the size of the blob (total
28   * size, header size included) and its checksum. Then it reads the rest of the
29   * blob [i.e size - sizeof(struct var_size_header) bytes], calculates the
30   * checksum and compares it with value read from the header.
31   */
32  struct var_size_header {
33  	uint32_t spl_size;
34  	uint32_t spl_checksum;
35  	uint32_t reserved[2];
36  };
37  
38  static const char *prog_name;
39  
40  static void write_to_file(int ofd, void *buffer, int size)
41  {
42  	if (write(ofd, buffer, size) == size)
43  		return;
44  
45  	fprintf(stderr, "%s: Failed to write to output file: %s\n",
46  		prog_name, strerror(errno));
47  	exit(EXIT_FAILURE);
48  }
49  
50  /*
51   * The argv is expected to include one optional parameter and two filenames:
52   * [--vs] IN OUT
53   *
54   * --vs - turns on the variable size SPL mode
55   * IN  - the u-boot SPL binary, usually u-boot-spl.bin
56   * OUT - the prepared SPL blob, usually ${BOARD}-spl.bin
57   *
58   * This utility first reads the "u-boot-spl.bin" into a buffer. In case of
59   * fixed size SPL the buffer size is exactly CHECKSUM_OFFSET (such that
60   * smaller u-boot-spl.bin gets padded with 0xff bytes, the larger than limit
61   * u-boot-spl.bin causes an error). For variable size SPL the buffer size is
62   * eqaul to size of the IN file.
63   *
64   * Then it calculates checksum of the buffer by just summing up all bytes.
65   * Then
66   *
67   * - for fixed size SPL the buffer is written into the output file and the
68   *   checksum is appended to the file in little endian format, which results
69   *   in checksum added exactly at CHECKSUM_OFFSET.
70   *
71   * - for variable size SPL the checksum and file size are stored in the
72   *   var_size_header structure (again, in little endian format) and the
73   *   structure is written into the output file. Then the buffer is written
74   *   into the output file.
75   */
76  int main(int argc, char **argv)
77  {
78  	unsigned char *buffer;
79  	int i, ifd, ofd;
80  	uint32_t checksum = 0;
81  	off_t	len;
82  	int	var_size_flag, read_size, count;
83  	struct stat stat;
84  	const int if_index = argc - 2; /* Input file name index in argv. */
85  	const int of_index = argc - 1; /* Output file name index in argv. */
86  
87  	/* Strip path off the program name. */
88  	prog_name = strrchr(argv[0], '/');
89  	if (prog_name)
90  		prog_name++;
91  	else
92  		prog_name = argv[0];
93  
94  	if ((argc < 3) ||
95  	    (argc > 4) ||
96  	    ((argc == 4) && strcmp(argv[1], "--vs"))) {
97  		fprintf(stderr, "Usage: %s [--vs] <infile> <outfile>\n",
98  			prog_name);
99  		exit(EXIT_FAILURE);
100  	}
101  
102  	/* four args mean variable size SPL wrapper is required */
103  	var_size_flag = (argc == 4);
104  
105  	ifd = open(argv[if_index], O_RDONLY);
106  	if (ifd < 0) {
107  		fprintf(stderr, "%s: Can't open %s: %s\n",
108  			prog_name, argv[if_index], strerror(errno));
109  		exit(EXIT_FAILURE);
110  	}
111  
112  	ofd = open(argv[of_index], O_WRONLY | O_CREAT | O_TRUNC, FILE_PERM);
113  	if (ofd < 0) {
114  		fprintf(stderr, "%s: Can't open %s: %s\n",
115  			prog_name, argv[of_index], strerror(errno));
116  		exit(EXIT_FAILURE);
117  	}
118  
119  	if (fstat(ifd, &stat)) {
120  		fprintf(stderr, "%s: Unable to get size of %s: %s\n",
121  			prog_name, argv[if_index], strerror(errno));
122  		exit(EXIT_FAILURE);
123  	}
124  
125  	len = stat.st_size;
126  
127  	if (var_size_flag) {
128  		read_size = len;
129  		count = len;
130  	} else {
131  		if (len > CHECKSUM_OFFSET) {
132  			fprintf(stderr,
133  				"%s: %s is too big (exceeds %d bytes)\n",
134  				prog_name, argv[if_index], CHECKSUM_OFFSET);
135  			exit(EXIT_FAILURE);
136  		}
137  		count = CHECKSUM_OFFSET;
138  		read_size = len;
139  	}
140  
141  	buffer = malloc(count);
142  	if (!buffer) {
143  		fprintf(stderr,
144  			"%s: Failed to allocate %d bytes to store %s\n",
145  			prog_name, count, argv[if_index]);
146  		exit(EXIT_FAILURE);
147  	}
148  
149  	if (read(ifd, buffer, read_size) != read_size) {
150  		fprintf(stderr, "%s: Can't read %s: %s\n",
151  			prog_name, argv[if_index], strerror(errno));
152  		exit(EXIT_FAILURE);
153  	}
154  
155  	/* Pad if needed with 0xff to make flashing faster. */
156  	if (read_size < count)
157  		memset((char *)buffer + read_size, 0xff, count - read_size);
158  
159  	for (i = 0, checksum = 0; i < count; i++)
160  		checksum += buffer[i];
161  	checksum = cpu_to_le32(checksum);
162  
163  	if (var_size_flag) {
164  		/* Prepare and write out the variable size SPL header. */
165  		struct var_size_header vsh;
166  		uint32_t spl_size;
167  
168  		memset(&vsh, 0, sizeof(vsh));
169  		memcpy(&vsh.spl_checksum, &checksum, sizeof(checksum));
170  
171  		spl_size = cpu_to_le32(count + sizeof(struct var_size_header));
172  		memcpy(&vsh.spl_size, &spl_size, sizeof(spl_size));
173  		write_to_file(ofd, &vsh, sizeof(vsh));
174  	}
175  
176  	write_to_file(ofd, buffer, count);
177  
178  	/* For fixed size SPL checksum is appended in the end. */
179  	if (!var_size_flag)
180  		write_to_file(ofd, &checksum, sizeof(checksum));
181  
182  	close(ifd);
183  	close(ofd);
184  	free(buffer);
185  
186  	return EXIT_SUCCESS;
187  }
188