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