]>
Commit | Line | Data |
---|---|---|
fab430be AT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright 2018 Linaro Limited | |
4 | * Author: AKASHI Takahiro | |
5 | */ | |
6 | ||
322c813f | 7 | #include <errno.h> |
fab430be AT |
8 | #include <getopt.h> |
9 | #include <malloc.h> | |
10 | #include <stdbool.h> | |
11 | #include <stdio.h> | |
12 | #include <stdlib.h> | |
13 | #include <string.h> | |
322c813f | 14 | #include <unistd.h> |
fab430be | 15 | #include <linux/types.h> |
322c813f SG |
16 | |
17 | #include <sys/mman.h> | |
fab430be AT |
18 | #include <sys/stat.h> |
19 | #include <sys/types.h> | |
20 | ||
322c813f SG |
21 | #include "fdt_host.h" |
22 | ||
fab430be AT |
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 | ||
322c813f SG |
32 | #define SIGNATURE_NODENAME "signature" |
33 | #define OVERLAY_NODENAME "__overlay__" | |
34 | ||
fab430be AT |
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'}, | |
322c813f SG |
55 | {"dtb", required_argument, NULL, 'D'}, |
56 | {"public key", required_argument, NULL, 'K'}, | |
57 | {"overlay", no_argument, NULL, 'O'}, | |
fab430be AT |
58 | {"help", no_argument, NULL, 'h'}, |
59 | {NULL, 0, NULL, 0}, | |
60 | }; | |
61 | ||
62 | static void print_usage(void) | |
63 | { | |
64 | printf("Usage: %s [options] <output file>\n" | |
65 | "Options:\n" | |
322c813f SG |
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", | |
fab430be AT |
75 | tool_name); |
76 | } | |
77 | ||
322c813f SG |
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 | ||
136 | static int add_public_key(const char *pkey_file, const char *dtb_file, | |
137 | bool overlay) | |
138 | { | |
139 | int ret; | |
140 | int srcfd = 0; | |
141 | int destfd = 0; | |
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 | goto err; | |
154 | } | |
155 | ||
156 | ret = fstat(srcfd, &pub_key); | |
157 | if (ret == -1) { | |
158 | fprintf(stderr, "%s: Can't stat %s: %s\n", | |
159 | __func__, pkey_file, strerror(errno)); | |
160 | goto err; | |
161 | } | |
162 | ||
163 | src_size = pub_key.st_size; | |
164 | ||
165 | /* mmap the public key esl file */ | |
166 | sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0); | |
167 | if ((sptr == MAP_FAILED) || (errno != 0)) { | |
168 | fprintf(stderr, "%s: Failed to mmap %s:%s\n", | |
169 | __func__, pkey_file, strerror(errno)); | |
170 | goto err; | |
171 | } | |
172 | ||
173 | /* Open the dest FDT */ | |
174 | destfd = open(dtb_file, O_RDWR); | |
175 | if (destfd == -1) { | |
176 | fprintf(stderr, "%s: Can't open %s: %s\n", | |
177 | __func__, dtb_file, strerror(errno)); | |
178 | goto err; | |
179 | } | |
180 | ||
181 | ret = fstat(destfd, &dtb); | |
182 | if (ret == -1) { | |
183 | fprintf(stderr, "%s: Can't stat %s: %s\n", | |
184 | __func__, dtb_file, strerror(errno)); | |
185 | goto err; | |
186 | } | |
187 | ||
188 | dtb.st_size += src_size + 0x30; | |
189 | if (ftruncate(destfd, dtb.st_size)) { | |
190 | fprintf(stderr, "%s: Can't expand %s: %s\n", | |
191 | __func__, dtb_file, strerror(errno)); | |
192 | goto err;; | |
193 | } | |
194 | ||
195 | errno = 0; | |
196 | /* mmap the dtb file */ | |
197 | dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, | |
198 | destfd, 0); | |
199 | if ((dptr == MAP_FAILED) || (errno != 0)) { | |
200 | fprintf(stderr, "%s: Failed to mmap %s:%s\n", | |
201 | __func__, dtb_file, strerror(errno)); | |
202 | goto err; | |
203 | } | |
204 | ||
205 | if (fdt_check_header(dptr)) { | |
206 | fprintf(stderr, "%s: Invalid FDT header\n", __func__); | |
207 | goto err; | |
208 | } | |
209 | ||
210 | ret = fdt_open_into(dptr, dptr, dtb.st_size); | |
211 | if (ret) { | |
212 | fprintf(stderr, "%s: Cannot expand FDT: %s\n", | |
213 | __func__, fdt_strerror(ret)); | |
214 | goto err; | |
215 | } | |
216 | ||
217 | /* Copy the esl file to the expanded FDT */ | |
218 | ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay); | |
219 | if (ret < 0) { | |
220 | fprintf(stderr, "%s: Unable to add public key to the FDT\n", | |
221 | __func__); | |
222 | goto err; | |
223 | } | |
224 | ||
225 | return 0; | |
226 | ||
227 | err: | |
228 | if (sptr) | |
229 | munmap(sptr, src_size); | |
230 | ||
231 | if (dptr) | |
232 | munmap(dptr, dtb.st_size); | |
233 | ||
234 | if (srcfd >= 0) | |
235 | close(srcfd); | |
236 | ||
237 | if (destfd >= 0) | |
238 | close(destfd); | |
239 | ||
240 | return -1; | |
241 | } | |
242 | ||
fab430be AT |
243 | static int create_fwbin(char *path, char *bin, efi_guid_t *guid, |
244 | unsigned long index, unsigned long instance) | |
245 | { | |
246 | struct efi_capsule_header header; | |
247 | struct efi_firmware_management_capsule_header capsule; | |
248 | struct efi_firmware_management_capsule_image_header image; | |
249 | FILE *f, *g; | |
250 | struct stat bin_stat; | |
251 | u8 *data; | |
252 | size_t size; | |
253 | u64 offset; | |
254 | ||
255 | #ifdef DEBUG | |
256 | printf("For output: %s\n", path); | |
257 | printf("\tbin: %s\n\ttype: %pUl\n" bin, guid); | |
258 | printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); | |
259 | #endif | |
260 | ||
261 | g = fopen(bin, "r"); | |
262 | if (!g) { | |
263 | printf("cannot open %s\n", bin); | |
264 | return -1; | |
265 | } | |
266 | if (stat(bin, &bin_stat) < 0) { | |
267 | printf("cannot determine the size of %s\n", bin); | |
268 | goto err_1; | |
269 | } | |
270 | data = malloc(bin_stat.st_size); | |
271 | if (!data) { | |
272 | printf("cannot allocate memory: %lx\n", bin_stat.st_size); | |
273 | goto err_1; | |
274 | } | |
275 | f = fopen(path, "w"); | |
276 | if (!f) { | |
277 | printf("cannot open %s\n", path); | |
278 | goto err_2; | |
279 | } | |
280 | header.capsule_guid = efi_guid_fm_capsule; | |
281 | header.header_size = sizeof(header); | |
450596f2 AT |
282 | /* TODO: The current implementation ignores flags */ |
283 | header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; | |
fab430be AT |
284 | header.capsule_image_size = sizeof(header) |
285 | + sizeof(capsule) + sizeof(u64) | |
286 | + sizeof(image) | |
287 | + bin_stat.st_size; | |
288 | ||
289 | size = fwrite(&header, 1, sizeof(header), f); | |
290 | if (size < sizeof(header)) { | |
291 | printf("write failed (%lx)\n", size); | |
292 | goto err_3; | |
293 | } | |
294 | ||
295 | capsule.version = 0x00000001; | |
296 | capsule.embedded_driver_count = 0; | |
297 | capsule.payload_item_count = 1; | |
298 | size = fwrite(&capsule, 1, sizeof(capsule), f); | |
299 | if (size < (sizeof(capsule))) { | |
300 | printf("write failed (%lx)\n", size); | |
301 | goto err_3; | |
302 | } | |
303 | offset = sizeof(capsule) + sizeof(u64); | |
304 | size = fwrite(&offset, 1, sizeof(offset), f); | |
305 | if (size < sizeof(offset)) { | |
306 | printf("write failed (%lx)\n", size); | |
307 | goto err_3; | |
308 | } | |
309 | ||
310 | image.version = 0x00000003; | |
311 | memcpy(&image.update_image_type_id, guid, sizeof(*guid)); | |
312 | image.update_image_index = index; | |
f7cd8b7b AT |
313 | image.reserved[0] = 0; |
314 | image.reserved[1] = 0; | |
315 | image.reserved[2] = 0; | |
fab430be AT |
316 | image.update_image_size = bin_stat.st_size; |
317 | image.update_vendor_code_size = 0; /* none */ | |
318 | image.update_hardware_instance = instance; | |
319 | image.image_capsule_support = 0; | |
320 | ||
321 | size = fwrite(&image, 1, sizeof(image), f); | |
322 | if (size < sizeof(image)) { | |
323 | printf("write failed (%lx)\n", size); | |
324 | goto err_3; | |
325 | } | |
326 | size = fread(data, 1, bin_stat.st_size, g); | |
327 | if (size < bin_stat.st_size) { | |
328 | printf("read failed (%lx)\n", size); | |
329 | goto err_3; | |
330 | } | |
331 | size = fwrite(data, 1, bin_stat.st_size, f); | |
332 | if (size < bin_stat.st_size) { | |
333 | printf("write failed (%lx)\n", size); | |
334 | goto err_3; | |
335 | } | |
336 | ||
337 | fclose(f); | |
338 | fclose(g); | |
339 | free(data); | |
340 | ||
341 | return 0; | |
342 | ||
343 | err_3: | |
344 | fclose(f); | |
345 | err_2: | |
346 | free(data); | |
347 | err_1: | |
348 | fclose(g); | |
349 | ||
350 | return -1; | |
351 | } | |
352 | ||
353 | /* | |
354 | * Usage: | |
355 | * $ mkeficapsule -f <firmware binary> <output file> | |
356 | */ | |
357 | int main(int argc, char **argv) | |
358 | { | |
359 | char *file; | |
322c813f SG |
360 | char *pkey_file; |
361 | char *dtb_file; | |
fab430be AT |
362 | efi_guid_t *guid; |
363 | unsigned long index, instance; | |
364 | int c, idx; | |
322c813f SG |
365 | int ret; |
366 | bool overlay = false; | |
fab430be AT |
367 | |
368 | file = NULL; | |
322c813f SG |
369 | pkey_file = NULL; |
370 | dtb_file = NULL; | |
fab430be AT |
371 | guid = NULL; |
372 | index = 0; | |
373 | instance = 0; | |
374 | for (;;) { | |
322c813f | 375 | c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx); |
fab430be AT |
376 | if (c == -1) |
377 | break; | |
378 | ||
379 | switch (c) { | |
380 | case 'f': | |
381 | if (file) { | |
382 | printf("Image already specified\n"); | |
383 | return -1; | |
384 | } | |
385 | file = optarg; | |
386 | guid = &efi_guid_image_type_uboot_fit; | |
387 | break; | |
388 | case 'r': | |
389 | if (file) { | |
390 | printf("Image already specified\n"); | |
391 | return -1; | |
392 | } | |
393 | file = optarg; | |
394 | guid = &efi_guid_image_type_uboot_raw; | |
395 | break; | |
396 | case 'i': | |
397 | index = strtoul(optarg, NULL, 0); | |
398 | break; | |
399 | case 'I': | |
400 | instance = strtoul(optarg, NULL, 0); | |
401 | break; | |
322c813f SG |
402 | case 'K': |
403 | if (pkey_file) { | |
404 | printf("Public Key already specified\n"); | |
405 | return -1; | |
406 | } | |
407 | pkey_file = optarg; | |
408 | break; | |
409 | case 'D': | |
410 | if (dtb_file) { | |
411 | printf("DTB file already specified\n"); | |
412 | return -1; | |
413 | } | |
414 | dtb_file = optarg; | |
415 | break; | |
416 | case 'O': | |
417 | overlay = true; | |
418 | break; | |
fab430be AT |
419 | case 'h': |
420 | print_usage(); | |
421 | return 0; | |
422 | } | |
423 | } | |
424 | ||
322c813f SG |
425 | /* need a fit image file or raw image file */ |
426 | if (!file && !pkey_file && !dtb_file) { | |
427 | printf("%s: %d\n", __func__, __LINE__); | |
fab430be AT |
428 | print_usage(); |
429 | return -1; | |
430 | } | |
431 | ||
322c813f SG |
432 | if (pkey_file && dtb_file) { |
433 | ret = add_public_key(pkey_file, dtb_file, overlay); | |
434 | if (ret == -1) { | |
435 | printf("Adding public key to the dtb failed\n"); | |
436 | return -1; | |
437 | } else { | |
438 | return 0; | |
439 | } | |
fab430be AT |
440 | } |
441 | ||
442 | if (create_fwbin(argv[optind], file, guid, index, instance) | |
443 | < 0) { | |
444 | printf("Creating firmware capsule failed\n"); | |
445 | return -1; | |
446 | } | |
447 | ||
448 | return 0; | |
449 | } |