1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2018 Linaro Limited
4  *		Author: AKASHI Takahiro
5  */
6 
7 #include <errno.h>
8 #include <getopt.h>
9 #include <malloc.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <linux/types.h>
16 
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 
21 #include "fdt_host.h"
22 
23 typedef __u8 u8;
24 typedef __u16 u16;
25 typedef __u32 u32;
26 typedef __u64 u64;
27 typedef __s16 s16;
28 typedef __s32 s32;
29 
30 #define aligned_u64 __aligned_u64
31 
32 #define SIGNATURE_NODENAME	"signature"
33 #define OVERLAY_NODENAME	"__overlay__"
34 
35 #ifndef __packed
36 #define __packed __attribute__((packed))
37 #endif
38 
39 #include <efi.h>
40 #include <efi_api.h>
41 
42 static const char *tool_name = "mkeficapsule";
43 
44 efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
45 efi_guid_t efi_guid_image_type_uboot_fit =
46 		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
47 efi_guid_t efi_guid_image_type_uboot_raw =
48 		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
49 
50 static struct option options[] = {
51 	{"fit", required_argument, NULL, 'f'},
52 	{"raw", required_argument, NULL, 'r'},
53 	{"index", required_argument, NULL, 'i'},
54 	{"instance", required_argument, NULL, 'I'},
55 	{"dtb", required_argument, NULL, 'D'},
56 	{"public key", required_argument, NULL, 'K'},
57 	{"overlay", no_argument, NULL, 'O'},
58 	{"help", no_argument, NULL, 'h'},
59 	{NULL, 0, NULL, 0},
60 };
61 
print_usage(void)62 static void print_usage(void)
63 {
64 	printf("Usage: %s [options] <output file>\n"
65 	       "Options:\n"
66 
67 	       "\t-f, --fit <fit image>       new FIT image file\n"
68 	       "\t-r, --raw <raw image>       new raw image file\n"
69 	       "\t-i, --index <index>         update image index\n"
70 	       "\t-I, --instance <instance>   update hardware instance\n"
71 	       "\t-K, --public-key <key file> public key esl file\n"
72 	       "\t-D, --dtb <dtb file>        dtb file\n"
73 	       "\t-O, --overlay               the dtb file is an overlay\n"
74 	       "\t-h, --help                  print a help message\n",
75 	       tool_name);
76 }
77 
fdt_add_pub_key_data(void * sptr,void * dptr,size_t key_size,bool overlay)78 static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
79 				bool overlay)
80 {
81 	int parent;
82 	int ov_node;
83 	int frag_node;
84 	int ret = 0;
85 
86 	if (overlay) {
87 		/*
88 		 * The signature would be stored in the
89 		 * first fragment node of the overlay
90 		 */
91 		frag_node = fdt_first_subnode(dptr, 0);
92 		if (frag_node == -FDT_ERR_NOTFOUND) {
93 			fprintf(stderr,
94 				"Couldn't find the fragment node: %s\n",
95 				fdt_strerror(frag_node));
96 			goto done;
97 		}
98 
99 		ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
100 		if (ov_node == -FDT_ERR_NOTFOUND) {
101 			fprintf(stderr,
102 				"Couldn't find the __overlay__ node: %s\n",
103 				fdt_strerror(ov_node));
104 			goto done;
105 		}
106 	} else {
107 		ov_node = 0;
108 	}
109 
110 	parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
111 	if (parent == -FDT_ERR_NOTFOUND) {
112 		parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
113 		if (parent < 0) {
114 			ret = parent;
115 			if (ret != -FDT_ERR_NOSPACE) {
116 				fprintf(stderr,
117 					"Couldn't create signature node: %s\n",
118 					fdt_strerror(parent));
119 			}
120 		}
121 	}
122 	if (ret)
123 		goto done;
124 
125 	/* Write the key to the FDT node */
126 	ret = fdt_setprop(dptr, parent, "capsule-key",
127 			  sptr, key_size);
128 
129 done:
130 	if (ret)
131 		ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
132 
133 	return ret;
134 }
135 
add_public_key(const char * pkey_file,const char * dtb_file,bool overlay)136 static int add_public_key(const char *pkey_file, const char *dtb_file,
137 			  bool overlay)
138 {
139 	int ret;
140 	int srcfd = -1;
141 	int destfd = -1;
142 	void *sptr = NULL;
143 	void *dptr = NULL;
144 	off_t src_size;
145 	struct stat pub_key;
146 	struct stat dtb;
147 
148 	/* Find out the size of the public key */
149 	srcfd = open(pkey_file, O_RDONLY);
150 	if (srcfd == -1) {
151 		fprintf(stderr, "%s: Can't open %s: %s\n",
152 			__func__, pkey_file, strerror(errno));
153 		ret = -1;
154 		goto err;
155 	}
156 
157 	ret = fstat(srcfd, &pub_key);
158 	if (ret == -1) {
159 		fprintf(stderr, "%s: Can't stat %s: %s\n",
160 			__func__, pkey_file, strerror(errno));
161 		ret = -1;
162 		goto err;
163 	}
164 
165 	src_size = pub_key.st_size;
166 
167 	/* mmap the public key esl file */
168 	sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
169 	if (sptr == MAP_FAILED) {
170 		fprintf(stderr, "%s: Failed to mmap %s:%s\n",
171 			__func__, pkey_file, strerror(errno));
172 		ret = -1;
173 		goto err;
174 	}
175 
176 	/* Open the dest FDT */
177 	destfd = open(dtb_file, O_RDWR);
178 	if (destfd == -1) {
179 		fprintf(stderr, "%s: Can't open %s: %s\n",
180 			__func__, dtb_file, strerror(errno));
181 		ret = -1;
182 		goto err;
183 	}
184 
185 	ret = fstat(destfd, &dtb);
186 	if (ret == -1) {
187 		fprintf(stderr, "%s: Can't stat %s: %s\n",
188 			__func__, dtb_file, strerror(errno));
189 		goto err;
190 	}
191 
192 	dtb.st_size += src_size + 0x30;
193 	if (ftruncate(destfd, dtb.st_size)) {
194 		fprintf(stderr, "%s: Can't expand %s: %s\n",
195 			__func__, dtb_file, strerror(errno));
196 		ret = -1;
197 		goto err;
198 	}
199 
200 	errno = 0;
201 	/* mmap the dtb file */
202 	dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
203 		    destfd, 0);
204 	if (dptr == MAP_FAILED) {
205 		fprintf(stderr, "%s: Failed to mmap %s:%s\n",
206 			__func__, dtb_file, strerror(errno));
207 		ret = -1;
208 		goto err;
209 	}
210 
211 	if (fdt_check_header(dptr)) {
212 		fprintf(stderr, "%s: Invalid FDT header\n", __func__);
213 		ret = -1;
214 		goto err;
215 	}
216 
217 	ret = fdt_open_into(dptr, dptr, dtb.st_size);
218 	if (ret) {
219 		fprintf(stderr, "%s: Cannot expand FDT: %s\n",
220 			__func__, fdt_strerror(ret));
221 		ret = -1;
222 		goto err;
223 	}
224 
225 	/* Copy the esl file to the expanded FDT */
226 	ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
227 	if (ret < 0) {
228 		fprintf(stderr, "%s: Unable to add public key to the FDT\n",
229 			__func__);
230 		ret = -1;
231 		goto err;
232 	}
233 
234 	ret = 0;
235 
236 err:
237 	if (sptr)
238 		munmap(sptr, src_size);
239 
240 	if (dptr)
241 		munmap(dptr, dtb.st_size);
242 
243 	if (srcfd != -1)
244 		close(srcfd);
245 
246 	if (destfd != -1)
247 		close(destfd);
248 
249 	return ret;
250 }
251 
create_fwbin(char * path,char * bin,efi_guid_t * guid,unsigned long index,unsigned long instance)252 static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
253 			unsigned long index, unsigned long instance)
254 {
255 	struct efi_capsule_header header;
256 	struct efi_firmware_management_capsule_header capsule;
257 	struct efi_firmware_management_capsule_image_header image;
258 	FILE *f, *g;
259 	struct stat bin_stat;
260 	u8 *data;
261 	size_t size;
262 	u64 offset;
263 
264 #ifdef DEBUG
265 	printf("For output: %s\n", path);
266 	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
267 	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
268 #endif
269 
270 	g = fopen(bin, "r");
271 	if (!g) {
272 		printf("cannot open %s\n", bin);
273 		return -1;
274 	}
275 	if (stat(bin, &bin_stat) < 0) {
276 		printf("cannot determine the size of %s\n", bin);
277 		goto err_1;
278 	}
279 	data = malloc(bin_stat.st_size);
280 	if (!data) {
281 		printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
282 		goto err_1;
283 	}
284 	f = fopen(path, "w");
285 	if (!f) {
286 		printf("cannot open %s\n", path);
287 		goto err_2;
288 	}
289 	header.capsule_guid = efi_guid_fm_capsule;
290 	header.header_size = sizeof(header);
291 	/* TODO: The current implementation ignores flags */
292 	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
293 	header.capsule_image_size = sizeof(header)
294 					+ sizeof(capsule) + sizeof(u64)
295 					+ sizeof(image)
296 					+ bin_stat.st_size;
297 
298 	size = fwrite(&header, 1, sizeof(header), f);
299 	if (size < sizeof(header)) {
300 		printf("write failed (%zx)\n", size);
301 		goto err_3;
302 	}
303 
304 	capsule.version = 0x00000001;
305 	capsule.embedded_driver_count = 0;
306 	capsule.payload_item_count = 1;
307 	size = fwrite(&capsule, 1, sizeof(capsule), f);
308 	if (size < (sizeof(capsule))) {
309 		printf("write failed (%zx)\n", size);
310 		goto err_3;
311 	}
312 	offset = sizeof(capsule) + sizeof(u64);
313 	size = fwrite(&offset, 1, sizeof(offset), f);
314 	if (size < sizeof(offset)) {
315 		printf("write failed (%zx)\n", size);
316 		goto err_3;
317 	}
318 
319 	image.version = 0x00000003;
320 	memcpy(&image.update_image_type_id, guid, sizeof(*guid));
321 	image.update_image_index = index;
322 	image.reserved[0] = 0;
323 	image.reserved[1] = 0;
324 	image.reserved[2] = 0;
325 	image.update_image_size = bin_stat.st_size;
326 	image.update_vendor_code_size = 0; /* none */
327 	image.update_hardware_instance = instance;
328 	image.image_capsule_support = 0;
329 
330 	size = fwrite(&image, 1, sizeof(image), f);
331 	if (size < sizeof(image)) {
332 		printf("write failed (%zx)\n", size);
333 		goto err_3;
334 	}
335 	size = fread(data, 1, bin_stat.st_size, g);
336 	if (size < bin_stat.st_size) {
337 		printf("read failed (%zx)\n", size);
338 		goto err_3;
339 	}
340 	size = fwrite(data, 1, bin_stat.st_size, f);
341 	if (size < bin_stat.st_size) {
342 		printf("write failed (%zx)\n", size);
343 		goto err_3;
344 	}
345 
346 	fclose(f);
347 	fclose(g);
348 	free(data);
349 
350 	return 0;
351 
352 err_3:
353 	fclose(f);
354 err_2:
355 	free(data);
356 err_1:
357 	fclose(g);
358 
359 	return -1;
360 }
361 
362 /*
363  * Usage:
364  *   $ mkeficapsule -f <firmware binary> <output file>
365  */
main(int argc,char ** argv)366 int main(int argc, char **argv)
367 {
368 	char *file;
369 	char *pkey_file;
370 	char *dtb_file;
371 	efi_guid_t *guid;
372 	unsigned long index, instance;
373 	int c, idx;
374 	int ret;
375 	bool overlay = false;
376 
377 	file = NULL;
378 	pkey_file = NULL;
379 	dtb_file = NULL;
380 	guid = NULL;
381 	index = 0;
382 	instance = 0;
383 	for (;;) {
384 		c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
385 		if (c == -1)
386 			break;
387 
388 		switch (c) {
389 		case 'f':
390 			if (file) {
391 				printf("Image already specified\n");
392 				return -1;
393 			}
394 			file = optarg;
395 			guid = &efi_guid_image_type_uboot_fit;
396 			break;
397 		case 'r':
398 			if (file) {
399 				printf("Image already specified\n");
400 				return -1;
401 			}
402 			file = optarg;
403 			guid = &efi_guid_image_type_uboot_raw;
404 			break;
405 		case 'i':
406 			index = strtoul(optarg, NULL, 0);
407 			break;
408 		case 'I':
409 			instance = strtoul(optarg, NULL, 0);
410 			break;
411 		case 'K':
412 			if (pkey_file) {
413 				printf("Public Key already specified\n");
414 				return -1;
415 			}
416 			pkey_file = optarg;
417 			break;
418 		case 'D':
419 			if (dtb_file) {
420 				printf("DTB file already specified\n");
421 				return -1;
422 			}
423 			dtb_file = optarg;
424 			break;
425 		case 'O':
426 			overlay = true;
427 			break;
428 		case 'h':
429 			print_usage();
430 			return 0;
431 		}
432 	}
433 
434 	/* need a fit image file or raw image file */
435 	if (!file && !pkey_file && !dtb_file) {
436 		print_usage();
437 		exit(EXIT_FAILURE);
438 	}
439 
440 	if (pkey_file && dtb_file) {
441 		ret = add_public_key(pkey_file, dtb_file, overlay);
442 		if (ret == -1) {
443 			printf("Adding public key to the dtb failed\n");
444 			exit(EXIT_FAILURE);
445 		} else {
446 			exit(EXIT_SUCCESS);
447 		}
448 	}
449 
450 	if (create_fwbin(argv[optind], file, guid, index, instance)
451 			< 0) {
452 		printf("Creating firmware capsule failed\n");
453 		exit(EXIT_FAILURE);
454 	}
455 
456 	exit(EXIT_SUCCESS);
457 }
458