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--fit <fit image> new FIT image file\n"
68 "\t--raw <raw image> new raw image file\n"
69 "\t--index <index> update image index\n"
70 "\t--instance <instance> update hardware instance\n"
71 "\t--public-key <key file> public key esl file\n"
72 "\t--dtb <dtb file> dtb file\n"
73 "\t--overlay the dtb file is an overlay\n"
74 "\t--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