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