blob: 243fd6e48370799dc08ed8fa573f5267edbf4078 [file] [log] [blame]
AKASHI Takahirofab430b2020-11-30 18:12:15 +09001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2018 Linaro Limited
4 * Author: AKASHI Takahiro
5 */
6
7#include <getopt.h>
8#include <malloc.h>
9#include <stdbool.h>
AKASHI Takahiro9e637862022-01-18 13:39:45 +090010#include <stdint.h>
AKASHI Takahirofab430b2020-11-30 18:12:15 +090011#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <linux/types.h>
Sughosh Ganu322c8132020-12-30 19:26:59 +053015
AKASHI Takahirofab430b2020-11-30 18:12:15 +090016#include <sys/stat.h>
17#include <sys/types.h>
18
19typedef __u8 u8;
20typedef __u16 u16;
21typedef __u32 u32;
22typedef __u64 u64;
23typedef __s16 s16;
24typedef __s32 s32;
25
26#define aligned_u64 __aligned_u64
27
28#ifndef __packed
29#define __packed __attribute__((packed))
30#endif
31
32#include <efi.h>
33#include <efi_api.h>
34
35static const char *tool_name = "mkeficapsule";
36
37efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
38efi_guid_t efi_guid_image_type_uboot_fit =
39 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
40efi_guid_t efi_guid_image_type_uboot_raw =
41 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
42
43static struct option options[] = {
44 {"fit", required_argument, NULL, 'f'},
45 {"raw", required_argument, NULL, 'r'},
46 {"index", required_argument, NULL, 'i'},
47 {"instance", required_argument, NULL, 'I'},
48 {"help", no_argument, NULL, 'h'},
49 {NULL, 0, NULL, 0},
50};
51
52static void print_usage(void)
53{
54 printf("Usage: %s [options] <output file>\n"
AKASHI Takahiro9e637862022-01-18 13:39:45 +090055 "Options:\n"
Sughosh Ganu322c8132020-12-30 19:26:59 +053056
AKASHI Takahiro9e637862022-01-18 13:39:45 +090057 "\t-f, --fit <fit image> new FIT image file\n"
58 "\t-r, --raw <raw image> new raw image file\n"
59 "\t-i, --index <index> update image index\n"
60 "\t-I, --instance <instance> update hardware instance\n"
61 "\t-h, --help print a help message\n",
62 tool_name);
AKASHI Takahirofab430b2020-11-30 18:12:15 +090063}
64
AKASHI Takahiro9e637862022-01-18 13:39:45 +090065/**
66 * read_bin_file - read a firmware binary file
67 * @bin: Path to a firmware binary file
68 * @data: Pointer to pointer of allocated buffer
69 * @bin_size: Size of allocated buffer
70 *
71 * Read out a content of binary, @bin, into @data.
72 * A caller should free @data.
73 *
74 * Return:
75 * * 0 - on success
76 * * -1 - on failure
77 */
78static int read_bin_file(char *bin, void **data, off_t *bin_size)
AKASHI Takahirofab430b2020-11-30 18:12:15 +090079{
AKASHI Takahiro9e637862022-01-18 13:39:45 +090080 FILE *g;
AKASHI Takahirofab430b2020-11-30 18:12:15 +090081 struct stat bin_stat;
AKASHI Takahiro9e637862022-01-18 13:39:45 +090082 void *buf;
AKASHI Takahirofab430b2020-11-30 18:12:15 +090083 size_t size;
AKASHI Takahiro9e637862022-01-18 13:39:45 +090084 int ret = 0;
AKASHI Takahirofab430b2020-11-30 18:12:15 +090085
86 g = fopen(bin, "r");
87 if (!g) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +090088 fprintf(stderr, "cannot open %s\n", bin);
AKASHI Takahirofab430b2020-11-30 18:12:15 +090089 return -1;
90 }
91 if (stat(bin, &bin_stat) < 0) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +090092 fprintf(stderr, "cannot determine the size of %s\n", bin);
AKASHI Takahiro9e637862022-01-18 13:39:45 +090093 ret = -1;
94 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +090095 }
AKASHI Takahiro9e637862022-01-18 13:39:45 +090096 if (bin_stat.st_size > SIZE_MAX) {
97 fprintf(stderr, "file size is too large for malloc: %s\n", bin);
98 ret = -1;
99 goto err;
100 }
101 buf = malloc(bin_stat.st_size);
102 if (!buf) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +0900103 fprintf(stderr, "cannot allocate memory: %zx\n",
104 (size_t)bin_stat.st_size);
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900105 ret = -1;
106 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900107 }
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900108
109 size = fread(buf, 1, bin_stat.st_size, g);
110 if (size < bin_stat.st_size) {
111 fprintf(stderr, "read failed (%zx)\n", size);
112 ret = -1;
113 goto err;
114 }
115
116 *data = buf;
117 *bin_size = bin_stat.st_size;
118err:
119 fclose(g);
120
121 return ret;
122}
123
124/**
125 * write_capsule_file - write a capsule file
126 * @bin: FILE stream
127 * @data: Pointer to data
128 * @bin_size: Size of data
129 *
130 * Write out data, @data, with the size @bin_size.
131 *
132 * Return:
133 * * 0 - on success
134 * * -1 - on failure
135 */
136static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
137{
138 size_t size_written;
139
140 size_written = fwrite(data, 1, size, f);
141 if (size_written < size) {
142 fprintf(stderr, "%s: write failed (%zx != %zx)\n", msg,
143 size_written, size);
144 return -1;
145 }
146
147 return 0;
148}
149
150/**
151 * create_fwbin - create an uefi capsule file
152 * @path: Path to a created capsule file
153 * @bin: Path to a firmware binary to encapsulate
154 * @guid: GUID of related FMP driver
155 * @index: Index number in capsule
156 * @instance: Instance number in capsule
157 * @mcount: Monotonic count in authentication information
158 * @private_file: Path to a private key file
159 * @cert_file: Path to a certificate file
160 *
161 * This function actually does the job of creating an uefi capsule file.
162 * All the arguments must be supplied.
163 * If either @private_file ror @cert_file is NULL, the capsule file
164 * won't be signed.
165 *
166 * Return:
167 * * 0 - on success
168 * * -1 - on failure
169 */
170static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
171 unsigned long index, unsigned long instance)
172{
173 struct efi_capsule_header header;
174 struct efi_firmware_management_capsule_header capsule;
175 struct efi_firmware_management_capsule_image_header image;
176 FILE *f;
177 void *data;
178 off_t bin_size;
179 u64 offset;
180 int ret;
181
182#ifdef DEBUG
183 printf("For output: %s\n", path);
184 printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
185 printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
186#endif
187
188 f = NULL;
189 data = NULL;
190 ret = -1;
191
192 /*
193 * read a firmware binary
194 */
195 if (read_bin_file(bin, &data, &bin_size))
196 goto err;
197
198 /*
199 * write a capsule file
200 */
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900201 f = fopen(path, "w");
202 if (!f) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +0900203 fprintf(stderr, "cannot open %s\n", path);
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900204 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900205 }
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900206
207 /*
208 * capsule file header
209 */
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900210 header.capsule_guid = efi_guid_fm_capsule;
211 header.header_size = sizeof(header);
AKASHI Takahiro450596f2020-11-30 18:12:16 +0900212 /* TODO: The current implementation ignores flags */
213 header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900214 header.capsule_image_size = sizeof(header)
215 + sizeof(capsule) + sizeof(u64)
216 + sizeof(image)
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900217 + bin_size;
218 if (write_capsule_file(f, &header, sizeof(header),
219 "Capsule header"))
220 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900221
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900222 /*
223 * firmware capsule header
224 * This capsule has only one firmware capsule image.
225 */
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900226 capsule.version = 0x00000001;
227 capsule.embedded_driver_count = 0;
228 capsule.payload_item_count = 1;
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900229 if (write_capsule_file(f, &capsule, sizeof(capsule),
230 "Firmware capsule header"))
231 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900232
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900233 offset = sizeof(capsule) + sizeof(u64);
234 if (write_capsule_file(f, &offset, sizeof(offset),
235 "Offset to capsule image"))
236 goto err;
237
238 /*
239 * firmware capsule image header
240 */
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900241 image.version = 0x00000003;
242 memcpy(&image.update_image_type_id, guid, sizeof(*guid));
243 image.update_image_index = index;
AKASHI Takahirof7cd8b72021-01-22 10:43:49 +0900244 image.reserved[0] = 0;
245 image.reserved[1] = 0;
246 image.reserved[2] = 0;
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900247 image.update_image_size = bin_size;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900248 image.update_vendor_code_size = 0; /* none */
249 image.update_hardware_instance = instance;
250 image.image_capsule_support = 0;
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900251 if (write_capsule_file(f, &image, sizeof(image),
252 "Firmware capsule image header"))
253 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900254
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900255 /*
256 * firmware binary
257 */
258 if (write_capsule_file(f, data, bin_size, "Firmware binary"))
259 goto err;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900260
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900261 ret = 0;
262err:
263 if (f)
264 fclose(f);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900265 free(data);
266
AKASHI Takahiro9e637862022-01-18 13:39:45 +0900267 return ret;
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900268}
269
270/*
271 * Usage:
272 * $ mkeficapsule -f <firmware binary> <output file>
273 */
274int main(int argc, char **argv)
275{
276 char *file;
277 efi_guid_t *guid;
278 unsigned long index, instance;
279 int c, idx;
280
281 file = NULL;
282 guid = NULL;
283 index = 0;
284 instance = 0;
285 for (;;) {
AKASHI Takahiro18cfbbb2021-10-07 15:23:30 +0900286 c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900287 if (c == -1)
288 break;
289
290 switch (c) {
291 case 'f':
292 if (file) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +0900293 fprintf(stderr, "Image already specified\n");
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900294 return -1;
295 }
296 file = optarg;
297 guid = &efi_guid_image_type_uboot_fit;
298 break;
299 case 'r':
300 if (file) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +0900301 fprintf(stderr, "Image already specified\n");
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900302 return -1;
303 }
304 file = optarg;
305 guid = &efi_guid_image_type_uboot_raw;
306 break;
307 case 'i':
308 index = strtoul(optarg, NULL, 0);
309 break;
310 case 'I':
311 instance = strtoul(optarg, NULL, 0);
312 break;
313 case 'h':
314 print_usage();
315 return 0;
316 }
317 }
318
AKASHI Takahiro18cfbbb2021-10-07 15:23:30 +0900319 /* need an output file */
320 if (argc != optind + 1) {
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900321 print_usage();
Sughosh Ganud33f3182021-01-22 20:34:56 +0530322 exit(EXIT_FAILURE);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900323 }
324
AKASHI Takahiro18cfbbb2021-10-07 15:23:30 +0900325 /* need a fit image file or raw image file */
326 if (!file) {
327 print_usage();
328 exit(EXIT_SUCCESS);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900329 }
330
331 if (create_fwbin(argv[optind], file, guid, index, instance)
332 < 0) {
AKASHI Takahirodf1ce602022-01-18 13:39:44 +0900333 fprintf(stderr, "Creating firmware capsule failed\n");
Sughosh Ganud33f3182021-01-22 20:34:56 +0530334 exit(EXIT_FAILURE);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900335 }
336
Sughosh Ganud33f3182021-01-22 20:34:56 +0530337 exit(EXIT_SUCCESS);
AKASHI Takahirofab430b2020-11-30 18:12:15 +0900338}