1 /*
2  * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdarg.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include "sptool.h"
16 
17 #define PAGE_SIZE		4096
18 
19 /*
20  * Entry describing Secure Partition package.
21  */
22 struct sp_pkg_info {
23 	/* Location of the files in the host's RAM. */
24 	void *img_data, *pm_data;
25 
26 	/* Size of the files. */
27 	uint32_t img_size, pm_size;
28 
29 	/* Location of the binary files inside the package output file */
30 	uint32_t img_offset, pm_offset;
31 };
32 
33 /*
34  * List of input provided by user
35  */
36 struct arg_list {
37 	char *usr_input;
38 	struct arg_list *next;
39 };
40 
41 /* Align an address to a power-of-two boundary. */
align_to(unsigned int address,unsigned int boundary)42 static unsigned int align_to(unsigned int address, unsigned int boundary)
43 {
44 	unsigned int mask = boundary - 1U;
45 
46 	if ((address & mask) != 0U)
47 		return (address + boundary) & ~mask;
48 	else
49 		return address;
50 }
51 
52 /* Allocate a memory area of 'size' bytes and zero it. */
xzalloc(size_t size,const char * msg)53 static void *xzalloc(size_t size, const char *msg)
54 {
55 	void *d;
56 
57 	d = malloc(size);
58 	if (d == NULL) {
59 		fprintf(stderr, "error: malloc: %s\n", msg);
60 		exit(1);
61 	}
62 
63 	memset(d, 0, size);
64 
65 	return d;
66 }
67 
68 /*
69  * Write 'size' bytes from 'buf' into the specified file stream.
70  * Exit the program on error.
71  */
xfwrite(void * buf,size_t size,FILE * fp)72 static void xfwrite(void *buf, size_t size, FILE *fp)
73 {
74 	if (fwrite(buf, 1, size, fp) != size) {
75 		fprintf(stderr, "error: Failed to write to output file.\n");
76 		exit(1);
77 	}
78 }
79 
80 /*
81  * Set the file position indicator for the specified file stream.
82  * Exit the program on error.
83  */
xfseek(FILE * fp,long offset,int whence)84 static void xfseek(FILE *fp, long offset, int whence)
85 {
86 	if (fseek(fp, offset, whence) != 0) {
87 		fprintf(stderr, "error: Failed to set file to offset 0x%lx (%d).\n",
88 		       offset, whence);
89 		perror(NULL);
90 		exit(1);
91 	}
92 }
93 
94 /*
95  * Free SP package structure
96  */
cleanup(struct sp_pkg_info * sp)97 static void cleanup(struct sp_pkg_info *sp)
98 {
99 
100 	if (sp != NULL) {
101 		if (sp->img_data != NULL) {
102 			free(sp->img_data);
103 		}
104 
105 		if (sp->pm_data != NULL) {
106 			free(sp->pm_data);
107 		}
108 
109 		free(sp);
110 
111 	}
112 }
113 
114 /*
115  * Free argument list structure
116  */
freelist(struct arg_list * head)117 static void freelist(struct arg_list *head)
118 {
119 	struct arg_list *tmp;
120 
121 	while (head != NULL) {
122 		tmp = head;
123 		head = head->next;
124 		free(tmp);
125 	}
126 }
127 
128 /*
129  * Append user inputs in argument list structure
130  */
append_user_input(struct arg_list ** head,char * args)131 static void append_user_input(struct arg_list **head, char *args)
132 {
133 	struct arg_list *tmp = *head;
134 
135 	if (tmp == NULL) {
136 		tmp = xzalloc(sizeof(struct arg_list),
137 				"Failed to allocate arg_list struct");
138 		tmp->usr_input = args;
139 		*head = tmp;
140 	} else {
141 		while (tmp->next != NULL) {
142 			tmp = tmp->next;
143 		}
144 		tmp->next = xzalloc(sizeof(struct arg_list),
145 				"Failed to allocate arg_list struct");
146 		tmp = tmp->next;
147 		tmp->usr_input = args;
148 	}
149 }
150 
151 /*
152  * Allocate a buffer big enough to store the content of the specified file and
153  * load the file into it. Fill 'size' with the file size. Exit the program on
154  * error.
155  */
load_file(const char * path,void ** ptr,uint32_t * size)156 static void load_file(const char *path, void **ptr, uint32_t *size)
157 {
158 	FILE *f = fopen(path, "rb");
159 	if (f == NULL) {
160 		fprintf(stderr, "error: %s couldn't be opened.\n", path);
161 		exit(1);
162 	}
163 
164 	xfseek(f, 0, SEEK_END);
165 	*size = ftell(f);
166 	if (*size == 0) {
167 		fprintf(stderr, "error: Size of %s is 0\n", path);
168 		exit(1);
169 	}
170 
171 	rewind(f);
172 
173 	*ptr = malloc(*size);
174 	if (*ptr == NULL) {
175 		fprintf(stderr, "error: Not enough memory to load %s\n", path);
176 		exit(1);
177 	}
178 
179 	if (fread(*ptr, *size, 1, f) != 1) {
180 		fprintf(stderr, "error: Couldn't read %s\n", path);
181 		exit(1);
182 	}
183 
184 	fclose(f);
185 }
186 
187 /*
188  * Parse the string containing input payloads and fill in the
189  * SP Package data structure.
190  */
load_sp_pm(char * path,struct sp_pkg_info ** sp_out)191 static void load_sp_pm(char *path, struct sp_pkg_info **sp_out)
192 {
193 	struct sp_pkg_info *sp_pkg;
194 
195 	char *split_mark = strstr(path, ":");
196 
197 	*split_mark = '\0';
198 
199 	char *sp_path = path;
200 	char *pm_path = split_mark + 1;
201 
202 	sp_pkg = xzalloc(sizeof(struct sp_pkg_info),
203 		"Failed to allocate sp_pkg_info struct");
204 
205 	load_file(pm_path, &sp_pkg->pm_data, &sp_pkg->pm_size);
206 	printf("\nLoaded SP Manifest file %s (%u bytes)\n", pm_path, sp_pkg->pm_size);
207 
208 	load_file(sp_path, &sp_pkg->img_data, &sp_pkg->img_size);
209 	printf("Loaded SP Image file %s (%u bytes)\n", sp_path, sp_pkg->img_size);
210 
211 	*sp_out = sp_pkg;
212 }
213 
214 /*
215  * Write SP package data structure into output file.
216  */
output_write(const char * path,struct sp_pkg_info * sp,bool header)217 static void output_write(const char *path, struct sp_pkg_info *sp, bool header)
218 {
219 	struct sp_pkg_header sp_header_info;
220 	unsigned int file_ptr = 0;
221 
222 	FILE *f = fopen(path, "wb");
223 	if (f == NULL) {
224 		fprintf(stderr, "error: Failed to open %s\n", path);
225 		exit(1);
226 	}
227 
228 	/* Reserve Header size */
229 	if (header) {
230 		file_ptr = sizeof(struct sp_pkg_header);
231 	}
232 
233 	/* Save partition manifest */
234 	xfseek(f, file_ptr, SEEK_SET);
235 	printf("Writing SP Manifest at offset 0x%x (%u bytes)\n",
236 	       file_ptr, sp->pm_size);
237 
238 	sp->pm_offset = file_ptr;
239 	xfwrite(sp->pm_data, sp->pm_size, f);
240 
241 	/* Save partition image aligned to Page size */
242 	file_ptr = align_to((sp->pm_offset + sp->pm_size), PAGE_SIZE);
243 	xfseek(f, file_ptr, SEEK_SET);
244 	printf("Writing SP Image at offset 0x%x (%u bytes)\n",
245 	       file_ptr, sp->img_size);
246 
247 	sp->img_offset = file_ptr;
248 	xfwrite(sp->img_data, sp->img_size, f);
249 
250 	/* Finally, write header, if needed */
251 	if (header) {
252 		sp_header_info.magic = SECURE_PARTITION_MAGIC;
253 		sp_header_info.version = 0x1;
254 		sp_header_info.img_offset = sp->img_offset;
255 		sp_header_info.img_size = sp->img_size;
256 		sp_header_info.pm_offset = sp->pm_offset;
257 		sp_header_info.pm_size = sp->pm_size;
258 
259 		xfseek(f, 0, SEEK_SET);
260 
261 		printf("Writing package header\n");
262 
263 		xfwrite(&sp_header_info, sizeof(struct sp_pkg_header), f);
264 	}
265 
266 	/* All information has been written now */
267 	printf("\nsptool: Built Secure Partition blob %s\n", path);
268 
269 	fclose(f);
270 }
271 
usage(void)272 static void usage(void)
273 {
274 	printf("usage: sptool ");
275 #ifdef VERSION
276 	printf(VERSION);
277 #else
278 	/* If built from sptool directory, VERSION is not set. */
279 	printf("version unknown");
280 #endif
281 	printf(" [<args>]\n\n");
282 
283 	printf("This tool takes as input set of image binary files and the\n"
284 	       "partition manifest blobs as input and generates set of\n"
285 	       "output package files\n"
286 	       "Usage example: sptool -i sp1.bin:sp1.dtb -o sp1.pkg\n"
287 	       "                      -i sp2.bin:sp2.dtb -o sp2.pkg ...\n\n");
288 	printf("Commands supported:\n");
289 	printf("  -o <path>            Set output file path.\n");
290 	printf("  -i <sp_path:pm_path> Add Secure Partition image and\n"
291 	       "                       Manifest blob (specified in two paths\n"
292 	       "                       separated by a colon).\n");
293 	printf("  -n                   Generate package without header\n");
294 	printf("  -h                   Show this message.\n");
295 	exit(1);
296 }
297 
main(int argc,char * argv[])298 int main(int argc, char *argv[])
299 {
300 	struct sp_pkg_info *sp_pkg = NULL;
301 	struct arg_list *in_head = NULL;
302 	struct arg_list *out_head = NULL;
303 	struct arg_list *in_list = NULL;
304 	struct arg_list *out_list = NULL;
305 	unsigned int match_counter = 0;
306 	bool need_header = true;
307 
308 	int ch;
309 
310 	if (argc <= 1) {
311 		fprintf(stderr, "error: File paths must be provided.\n\n");
312 		usage();
313 		return 1;
314 	}
315 
316 	while ((ch = getopt(argc, argv, "hni:o:")) != -1) {
317 		switch (ch) {
318 		case 'i':
319 			append_user_input(&in_head, optarg);
320 			match_counter++;
321 			break;
322 		case 'o':
323 			append_user_input(&out_head, optarg);
324 			match_counter--;
325 			break;
326 		case 'n':
327 			need_header = false;
328 			break;
329 		case 'h':
330 		default:
331 			usage();
332 		}
333 	}
334 
335 	if (match_counter) {
336 		fprintf(stderr, "error: Input/Output count mismatch.\n\n");
337 		freelist(in_head);
338 		freelist(out_head);
339 		usage();
340 		return 1;
341 	}
342 
343 	in_list = in_head;
344 	out_list = out_head;
345 	while (in_list != NULL) {
346 		load_sp_pm(in_list->usr_input, &sp_pkg);
347 		output_write(out_list->usr_input, sp_pkg, need_header);
348 		in_list = in_list->next;
349 		out_list = out_list->next;
350 	}
351 
352 	argc -= optind;
353 	argv += optind;
354 
355 	cleanup(sp_pkg);
356 	freelist(in_head);
357 	freelist(out_head);
358 
359 	return 0;
360 }
361