blob: b8fc6069b584420a0a04f15bca7aefacc4f770d7 [file] [log] [blame]
AKASHI Takahiro19122aa2020-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>
AKASHI Takahiroc246b762022-02-09 19:10:35 +09008#include <pe.h>
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09009#include <stdbool.h>
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090010#include <stdint.h>
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090011#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <linux/types.h>
Sughosh Ganu079fcf22020-12-30 19:26:59 +053015
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090016#include <sys/stat.h>
17#include <sys/types.h>
AKASHI Takahiroba212432022-02-09 19:10:39 +090018#include <uuid/uuid.h>
AKASHI Takahiroc246b762022-02-09 19:10:35 +090019#include <linux/kconfig.h>
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090020
AKASHI Takahiroc246b762022-02-09 19:10:35 +090021#include <gnutls/gnutls.h>
22#include <gnutls/pkcs7.h>
23#include <gnutls/abstract.h>
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090024
AKASHI Takahiroc246b762022-02-09 19:10:35 +090025#include "eficapsule.h"
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090026
27static const char *tool_name = "mkeficapsule";
28
29efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
AKASHI Takahiroc246b762022-02-09 19:10:35 +090030efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
31
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +053032static const char *opts_short = "g:i:I:v:p:c:m:o:dhARD";
Sughosh Ganub39405d2022-10-21 18:16:06 +053033
34enum {
35 CAPSULE_NORMAL_BLOB = 0,
36 CAPSULE_ACCEPT,
37 CAPSULE_REVERT,
38} capsule_type;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090039
40static struct option options[] = {
AKASHI Takahiroba212432022-02-09 19:10:39 +090041 {"guid", required_argument, NULL, 'g'},
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090042 {"index", required_argument, NULL, 'i'},
43 {"instance", required_argument, NULL, 'I'},
Masahisa Kojimade87ca02023-06-07 14:41:56 +090044 {"fw-version", required_argument, NULL, 'v'},
AKASHI Takahiroc246b762022-02-09 19:10:35 +090045 {"private-key", required_argument, NULL, 'p'},
46 {"certificate", required_argument, NULL, 'c'},
47 {"monotonic-count", required_argument, NULL, 'm'},
48 {"dump-sig", no_argument, NULL, 'd'},
Sughosh Ganub39405d2022-10-21 18:16:06 +053049 {"fw-accept", no_argument, NULL, 'A'},
50 {"fw-revert", no_argument, NULL, 'R'},
Sughosh Ganuc0676382022-10-21 18:16:07 +053051 {"capoemflag", required_argument, NULL, 'o'},
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +053052 {"dump-capsule", no_argument, NULL, 'D'},
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090053 {"help", no_argument, NULL, 'h'},
54 {NULL, 0, NULL, 0},
55};
56
57static void print_usage(void)
58{
AKASHI Takahiroba212432022-02-09 19:10:39 +090059 fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090060 "Options:\n"
Sughosh Ganu079fcf22020-12-30 19:26:59 +053061
AKASHI Takahiroba212432022-02-09 19:10:39 +090062 "\t-g, --guid <guid string> guid for image blob type\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090063 "\t-i, --index <index> update image index\n"
64 "\t-I, --instance <instance> update hardware instance\n"
Masahisa Kojimade87ca02023-06-07 14:41:56 +090065 "\t-v, --fw-version <version> firmware version\n"
AKASHI Takahiroc246b762022-02-09 19:10:35 +090066 "\t-p, --private-key <privkey file> private key file\n"
67 "\t-c, --certificate <cert file> signer's certificate file\n"
68 "\t-m, --monotonic-count <count> monotonic count\n"
69 "\t-d, --dump_sig dump signature (*.p7)\n"
Sughosh Ganub39405d2022-10-21 18:16:06 +053070 "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n"
71 "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n"
Sughosh Ganuc0676382022-10-21 18:16:07 +053072 "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n"
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +053073 "\t-D, --dump-capsule dump the contents of the capsule headers\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090074 "\t-h, --help print a help message\n",
75 tool_name);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090076}
77
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090078/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +090079 * auth_context - authentication context
80 * @key_file: Path to a private key file
81 * @cert_file: Path to a certificate file
82 * @image_data: Pointer to firmware data
83 * @image_size: Size of firmware data
84 * @auth: Authentication header
85 * @sig_data: Signature data
86 * @sig_size: Size of signature data
87 *
88 * Data structure used in create_auth_data(). @key_file through
89 * @image_size are input parameters. @auth, @sig_data and @sig_size
90 * are filled in by create_auth_data().
91 */
92struct auth_context {
93 char *key_file;
94 char *cert_file;
95 uint8_t *image_data;
96 size_t image_size;
97 struct efi_firmware_image_authentication auth;
98 uint8_t *sig_data;
99 size_t sig_size;
100};
101
102static int dump_sig;
103
104/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900105 * read_bin_file - read a firmware binary file
106 * @bin: Path to a firmware binary file
107 * @data: Pointer to pointer of allocated buffer
108 * @bin_size: Size of allocated buffer
109 *
110 * Read out a content of binary, @bin, into @data.
111 * A caller should free @data.
112 *
113 * Return:
114 * * 0 - on success
115 * * -1 - on failure
116 */
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900117static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900118{
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900119 FILE *g;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900120 struct stat bin_stat;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900121 void *buf;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900122 size_t size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900123 int ret = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900124
125 g = fopen(bin, "r");
126 if (!g) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900127 fprintf(stderr, "cannot open %s\n", bin);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900128 return -1;
129 }
130 if (stat(bin, &bin_stat) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900131 fprintf(stderr, "cannot determine the size of %s\n", bin);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900132 ret = -1;
133 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900134 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900135 if (bin_stat.st_size > SIZE_MAX) {
136 fprintf(stderr, "file size is too large for malloc: %s\n", bin);
137 ret = -1;
138 goto err;
139 }
140 buf = malloc(bin_stat.st_size);
141 if (!buf) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900142 fprintf(stderr, "cannot allocate memory: %zx\n",
143 (size_t)bin_stat.st_size);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900144 ret = -1;
145 goto err;
146 }
147
148 size = fread(buf, 1, bin_stat.st_size, g);
149 if (size < bin_stat.st_size) {
150 fprintf(stderr, "read failed (%zx)\n", size);
151 ret = -1;
152 goto err;
153 }
154
155 *data = buf;
156 *bin_size = bin_stat.st_size;
157err:
158 fclose(g);
159
160 return ret;
161}
162
163/**
164 * write_capsule_file - write a capsule file
165 * @bin: FILE stream
166 * @data: Pointer to data
167 * @bin_size: Size of data
168 *
169 * Write out data, @data, with the size @bin_size.
170 *
171 * Return:
172 * * 0 - on success
173 * * -1 - on failure
174 */
175static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
176{
177 size_t size_written;
178
179 size_written = fwrite(data, 1, size, f);
180 if (size_written < size) {
181 fprintf(stderr, "%s: write failed (%zx != %zx)\n", msg,
182 size_written, size);
183 return -1;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900184 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900185
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900186 return 0;
187}
188
189/**
190 * create_auth_data - compose authentication data in capsule
191 * @auth_context: Pointer to authentication context
192 *
193 * Fill up an authentication header (.auth) and signature data (.sig_data)
194 * in @auth_context, using library functions from openssl.
195 * All the parameters in @auth_context must be filled in by a caller.
196 *
197 * Return:
198 * * 0 - on success
199 * * -1 - on failure
200 */
201static int create_auth_data(struct auth_context *ctx)
202{
203 gnutls_datum_t cert;
204 gnutls_datum_t key;
205 off_t file_size;
206 gnutls_privkey_t pkey;
207 gnutls_x509_crt_t x509;
208 gnutls_pkcs7_t pkcs7;
209 gnutls_datum_t data;
210 gnutls_datum_t signature;
211 int ret;
212
213 ret = read_bin_file(ctx->cert_file, &cert.data, &file_size);
214 if (ret < 0)
215 return -1;
216 if (file_size > UINT_MAX)
217 return -1;
218 cert.size = file_size;
219
220 ret = read_bin_file(ctx->key_file, &key.data, &file_size);
221 if (ret < 0)
222 return -1;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900223 if (file_size > UINT_MAX)
224 return -1;
225 key.size = file_size;
226
227 /*
228 * For debugging,
229 * gnutls_global_set_time_function(mytime);
230 * gnutls_global_set_log_function(tls_log_func);
231 * gnutls_global_set_log_level(6);
232 */
233
234 ret = gnutls_privkey_init(&pkey);
235 if (ret < 0) {
236 fprintf(stderr, "error in gnutls_privkey_init(): %s\n",
237 gnutls_strerror(ret));
238 return -1;
239 }
240
241 ret = gnutls_x509_crt_init(&x509);
242 if (ret < 0) {
243 fprintf(stderr, "error in gnutls_x509_crt_init(): %s\n",
244 gnutls_strerror(ret));
245 return -1;
246 }
247
248 /* load a private key */
249 ret = gnutls_privkey_import_x509_raw(pkey, &key, GNUTLS_X509_FMT_PEM,
250 0, 0);
251 if (ret < 0) {
252 fprintf(stderr,
253 "error in gnutls_privkey_import_x509_raw(): %s\n",
254 gnutls_strerror(ret));
255 return -1;
256 }
257
258 /* load x509 certificate */
259 ret = gnutls_x509_crt_import(x509, &cert, GNUTLS_X509_FMT_PEM);
260 if (ret < 0) {
261 fprintf(stderr, "error in gnutls_x509_crt_import(): %s\n",
262 gnutls_strerror(ret));
263 return -1;
264 }
265
266 /* generate a PKCS #7 structure */
267 ret = gnutls_pkcs7_init(&pkcs7);
268 if (ret < 0) {
269 fprintf(stderr, "error in gnutls_pkcs7_init(): %s\n",
270 gnutls_strerror(ret));
271 return -1;
272 }
273
274 /* sign */
275 /*
276 * Data should have
277 * * firmware image
278 * * monotonic count
279 * in this order!
280 * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
281 */
282 data.size = ctx->image_size + sizeof(ctx->auth.monotonic_count);
283 data.data = malloc(data.size);
284 if (!data.data) {
285 fprintf(stderr, "allocating memory (0x%x) failed\n", data.size);
286 return -1;
287 }
288 memcpy(data.data, ctx->image_data, ctx->image_size);
289 memcpy(data.data + ctx->image_size, &ctx->auth.monotonic_count,
290 sizeof(ctx->auth.monotonic_count));
291
292 ret = gnutls_pkcs7_sign(pkcs7, x509, pkey, &data, NULL, NULL,
293 GNUTLS_DIG_SHA256,
294 /* GNUTLS_PKCS7_EMBED_DATA? */
295 GNUTLS_PKCS7_INCLUDE_CERT |
296 GNUTLS_PKCS7_INCLUDE_TIME);
297 if (ret < 0) {
298 fprintf(stderr, "error in gnutls_pkcs7)sign(): %s\n",
299 gnutls_strerror(ret));
300 return -1;
301 }
302
303 /* export */
304 ret = gnutls_pkcs7_export2(pkcs7, GNUTLS_X509_FMT_DER, &signature);
305 if (ret < 0) {
306 fprintf(stderr, "error in gnutls_pkcs7_export2: %s\n",
307 gnutls_strerror(ret));
308 return -1;
309 }
310 ctx->sig_data = signature.data;
311 ctx->sig_size = signature.size;
312
313 /* fill auth_info */
314 ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
315 + ctx->sig_size;
316 ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
317 ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
318 memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
319 sizeof(efi_guid_cert_type_pkcs7));
320
321 /*
322 * For better clean-ups,
323 * gnutls_pkcs7_deinit(pkcs7);
324 * gnutls_privkey_deinit(pkey);
325 * gnutls_x509_crt_deinit(x509);
326 * free(cert.data);
327 * free(key.data);
328 * if error
329 * gnutls_free(signature.data);
330 */
331
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900332 return 0;
333}
334
335/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900336 * dump_signature - dump out a signature
337 * @path: Path to a capsule file
338 * @signature: Signature data
339 * @sig_size: Size of signature data
340 *
341 * Signature data pointed to by @signature will be saved into
342 * a file whose file name is @path with ".p7" suffix.
343 *
344 * Return:
345 * * 0 - on success
346 * * -1 - on failure
347 */
348static int dump_signature(const char *path, uint8_t *signature, size_t sig_size)
349{
350 char *sig_path;
351 FILE *f;
352 size_t size;
353 int ret = -1;
354
355 sig_path = malloc(strlen(path) + 3 + 1);
356 if (!sig_path)
357 return ret;
358
359 sprintf(sig_path, "%s.p7", path);
360 f = fopen(sig_path, "w");
361 if (!f)
362 goto err;
363
364 size = fwrite(signature, 1, sig_size, f);
365 if (size == sig_size)
366 ret = 0;
367
368 fclose(f);
369err:
370 free(sig_path);
371 return ret;
372}
373
374/**
375 * free_sig_data - free out signature data
376 * @ctx: Pointer to authentication context
377 *
378 * Free signature data allocated in create_auth_data().
379 */
380static void free_sig_data(struct auth_context *ctx)
381{
382 if (ctx->sig_size)
383 gnutls_free(ctx->sig_data);
384}
385
386/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900387 * create_fwbin - create an uefi capsule file
388 * @path: Path to a created capsule file
389 * @bin: Path to a firmware binary to encapsulate
390 * @guid: GUID of related FMP driver
391 * @index: Index number in capsule
392 * @instance: Instance number in capsule
393 * @mcount: Monotonic count in authentication information
394 * @private_file: Path to a private key file
395 * @cert_file: Path to a certificate file
Sughosh Ganuc0676382022-10-21 18:16:07 +0530396 * @oemflags: Capsule OEM Flags, bits 0-15
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900397 *
398 * This function actually does the job of creating an uefi capsule file.
399 * All the arguments must be supplied.
400 * If either @private_file ror @cert_file is NULL, the capsule file
401 * won't be signed.
402 *
403 * Return:
404 * * 0 - on success
405 * * -1 - on failure
406 */
407static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900408 unsigned long index, unsigned long instance,
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900409 struct fmp_payload_header_params *fmp_ph_params,
Sughosh Ganuc0676382022-10-21 18:16:07 +0530410 uint64_t mcount, char *privkey_file, char *cert_file,
411 uint16_t oemflags)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900412{
413 struct efi_capsule_header header;
414 struct efi_firmware_management_capsule_header capsule;
415 struct efi_firmware_management_capsule_image_header image;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900416 struct auth_context auth_context;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900417 FILE *f;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900418 uint8_t *data, *new_data, *buf;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900419 off_t bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900420 uint64_t offset;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900421 int ret;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900422 struct fmp_payload_header payload_header;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900423
424#ifdef DEBUG
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900425 fprintf(stderr, "For output: %s\n", path);
426 fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
427 fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900428#endif
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900429 auth_context.sig_size = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900430 f = NULL;
431 data = NULL;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900432 new_data = NULL;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900433 ret = -1;
434
435 /*
436 * read a firmware binary
437 */
438 if (read_bin_file(bin, &data, &bin_size))
439 goto err;
440
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900441 buf = data;
442
443 /* insert fmp payload header right before the payload */
444 if (fmp_ph_params->have_header) {
445 new_data = malloc(bin_size + sizeof(payload_header));
446 if (!new_data)
447 goto err;
448
449 payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
450 payload_header.header_size = sizeof(payload_header);
451 payload_header.fw_version = fmp_ph_params->fw_version;
452 payload_header.lowest_supported_version = 0; /* not used */
453 memcpy(new_data, &payload_header, sizeof(payload_header));
454 memcpy(new_data + sizeof(payload_header), data, bin_size);
455 buf = new_data;
456 bin_size += sizeof(payload_header);
457 }
458
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900459 /* first, calculate signature to determine its size */
460 if (privkey_file && cert_file) {
461 auth_context.key_file = privkey_file;
462 auth_context.cert_file = cert_file;
463 auth_context.auth.monotonic_count = mcount;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900464 auth_context.image_data = buf;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900465 auth_context.image_size = bin_size;
466
467 if (create_auth_data(&auth_context)) {
468 fprintf(stderr, "Signing firmware image failed\n");
469 goto err;
470 }
471
472 if (dump_sig &&
473 dump_signature(path, auth_context.sig_data,
474 auth_context.sig_size)) {
475 fprintf(stderr, "Creating signature file failed\n");
476 goto err;
477 }
478 }
479
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900480 /*
481 * write a capsule file
482 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900483 f = fopen(path, "w");
484 if (!f) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900485 fprintf(stderr, "cannot open %s\n", path);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900486 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900487 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900488
489 /*
490 * capsule file header
491 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900492 header.capsule_guid = efi_guid_fm_capsule;
493 header.header_size = sizeof(header);
AKASHI Takahiro0f626ce2020-11-30 18:12:16 +0900494 /* TODO: The current implementation ignores flags */
495 header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530496 if (oemflags)
497 header.flags |= oemflags;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900498 header.capsule_image_size = sizeof(header)
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900499 + sizeof(capsule) + sizeof(uint64_t)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900500 + sizeof(image)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900501 + bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900502 if (auth_context.sig_size)
503 header.capsule_image_size += sizeof(auth_context.auth)
504 + auth_context.sig_size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900505 if (write_capsule_file(f, &header, sizeof(header),
506 "Capsule header"))
507 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900508
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900509 /*
510 * firmware capsule header
511 * This capsule has only one firmware capsule image.
512 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900513 capsule.version = 0x00000001;
514 capsule.embedded_driver_count = 0;
515 capsule.payload_item_count = 1;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900516 if (write_capsule_file(f, &capsule, sizeof(capsule),
517 "Firmware capsule header"))
518 goto err;
519
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900520 offset = sizeof(capsule) + sizeof(uint64_t);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900521 if (write_capsule_file(f, &offset, sizeof(offset),
522 "Offset to capsule image"))
523 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900524
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900525 /*
526 * firmware capsule image header
527 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900528 image.version = 0x00000003;
529 memcpy(&image.update_image_type_id, guid, sizeof(*guid));
530 image.update_image_index = index;
AKASHI Takahiroa4c14aa2021-01-22 10:43:49 +0900531 image.reserved[0] = 0;
532 image.reserved[1] = 0;
533 image.reserved[2] = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900534 image.update_image_size = bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900535 if (auth_context.sig_size)
536 image.update_image_size += sizeof(auth_context.auth)
537 + auth_context.sig_size;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900538 image.update_vendor_code_size = 0; /* none */
539 image.update_hardware_instance = instance;
540 image.image_capsule_support = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900541 if (auth_context.sig_size)
542 image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900543 if (write_capsule_file(f, &image, sizeof(image),
544 "Firmware capsule image header"))
545 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900546
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900547 /*
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900548 * signature
549 */
550 if (auth_context.sig_size) {
551 if (write_capsule_file(f, &auth_context.auth,
552 sizeof(auth_context.auth),
553 "Authentication header"))
554 goto err;
555
556 if (write_capsule_file(f, auth_context.sig_data,
557 auth_context.sig_size, "Signature"))
558 goto err;
559 }
560
561 /*
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900562 * firmware binary
563 */
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900564 if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900565 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900566
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900567 ret = 0;
568err:
569 if (f)
570 fclose(f);
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900571 free_sig_data(&auth_context);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900572 free(data);
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900573 free(new_data);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900574
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900575 return ret;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900576}
577
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900578/**
AKASHI Takahiroba212432022-02-09 19:10:39 +0900579 * convert_uuid_to_guid() - convert UUID to GUID
580 * @buf: UUID binary
581 *
582 * UUID and GUID have the same data structure, but their binary
583 * formats are different due to the endianness. See lib/uuid.c.
584 * Since uuid_parse() can handle only UUID, this function must
585 * be called to get correct data for GUID when parsing a string.
586 *
587 * The correct data will be returned in @buf.
588 */
589void convert_uuid_to_guid(unsigned char *buf)
590{
591 unsigned char c;
592
593 c = buf[0];
594 buf[0] = buf[3];
595 buf[3] = c;
596 c = buf[1];
597 buf[1] = buf[2];
598 buf[2] = c;
599
600 c = buf[4];
601 buf[4] = buf[5];
602 buf[5] = c;
603
604 c = buf[6];
605 buf[6] = buf[7];
606 buf[7] = c;
607}
608
Sughosh Ganub39405d2022-10-21 18:16:06 +0530609static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept)
610{
611 struct efi_capsule_header header = { 0 };
612 FILE *f = NULL;
613 int ret = -1;
614 efi_guid_t fw_accept_guid = FW_ACCEPT_OS_GUID;
615 efi_guid_t fw_revert_guid = FW_REVERT_OS_GUID;
616 efi_guid_t capsule_guid;
617
618 f = fopen(path, "w");
619 if (!f) {
620 fprintf(stderr, "cannot open %s\n", path);
621 goto err;
622 }
623
624 capsule_guid = fw_accept ? fw_accept_guid : fw_revert_guid;
625
626 memcpy(&header.capsule_guid, &capsule_guid, sizeof(efi_guid_t));
627 header.header_size = sizeof(header);
628 header.flags = 0;
629
630 header.capsule_image_size = fw_accept ?
631 sizeof(header) + sizeof(efi_guid_t) : sizeof(header);
632
633 if (write_capsule_file(f, &header, sizeof(header),
634 "Capsule header"))
635 goto err;
636
637 if (fw_accept) {
638 if (write_capsule_file(f, guid, sizeof(*guid),
639 "FW Accept Capsule Payload"))
640 goto err;
641 }
642
643 ret = 0;
644
645err:
646 if (f)
647 fclose(f);
648
649 return ret;
650}
651
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530652static void print_guid(void *ptr)
653{
654 int i;
655 efi_guid_t *guid = ptr;
656 const uint8_t seq[] = {
657 3, 2, 1, 0, '-', 5, 4, '-', 7, 6,
658 '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 };
659
660 for (i = 0; i < ARRAY_SIZE(seq); i++) {
661 if (seq[i] == '-')
662 putchar(seq[i]);
663 else
664 printf("%02X", guid->b[seq[i]]);
665 }
666
667 printf("\n");
668}
669
670static uint32_t dump_fmp_payload_header(
671 struct fmp_payload_header *fmp_payload_hdr)
672{
673 if (fmp_payload_hdr->signature == FMP_PAYLOAD_HDR_SIGNATURE) {
674 printf("--------\n");
675 printf("FMP_PAYLOAD_HDR.SIGNATURE\t\t\t: %08X\n",
676 FMP_PAYLOAD_HDR_SIGNATURE);
677 printf("FMP_PAYLOAD_HDR.HEADER_SIZE\t\t\t: %08X\n",
678 fmp_payload_hdr->header_size);
679 printf("FMP_PAYLOAD_HDR.FW_VERSION\t\t\t: %08X\n",
680 fmp_payload_hdr->fw_version);
681 printf("FMP_PAYLOAD_HDR.LOWEST_SUPPORTED_VERSION\t: %08X\n",
682 fmp_payload_hdr->lowest_supported_version);
683 return fmp_payload_hdr->header_size;
684 }
685
686 return 0;
687}
688
689static void dump_capsule_auth_header(
690 struct efi_firmware_image_authentication *capsule_auth_hdr)
691{
692 printf("EFI_FIRMWARE_IMAGE_AUTH.MONOTONIC_COUNT\t\t: %08lX\n",
693 capsule_auth_hdr->monotonic_count);
694 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.dwLENGTH\t: %08X\n",
695 capsule_auth_hdr->auth_info.hdr.dwLength);
696 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION\t: %08X\n",
697 capsule_auth_hdr->auth_info.hdr.wRevision);
698 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE\t: %08X\n",
699 capsule_auth_hdr->auth_info.hdr.wCertificateType);
700 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE\t: ");
701 print_guid(&capsule_auth_hdr->auth_info.cert_type);
702}
703
704static void dump_fmp_capsule_image_header(
705 struct efi_firmware_management_capsule_image_header *image_hdr)
706{
707 void *capsule_auth_hdr;
708 void *fmp_payload_hdr;
709 uint64_t signature_size = 0;
710 uint32_t payload_size = 0;
711 uint32_t fmp_payload_hdr_size = 0;
712 struct efi_firmware_image_authentication *auth_hdr;
713
714 printf("--------\n");
715 printf("FMP_CAPSULE_IMAGE_HDR.VERSION\t\t\t: %08X\n",
716 image_hdr->version);
717 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID\t: ");
718 print_guid(&image_hdr->update_image_type_id);
719 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX\t: %08X\n",
720 image_hdr->update_image_index);
721 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_SIZE\t\t: %08X\n",
722 image_hdr->update_image_size);
723 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_VENDOR_CODE_SIZE\t: %08X\n",
724 image_hdr->update_vendor_code_size);
725 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_HARDWARE_INSTANCE\t: %08lX\n",
726 image_hdr->update_hardware_instance);
727 printf("FMP_CAPSULE_IMAGE_HDR.IMAGE_CAPSULE_SUPPORT\t: %08lX\n",
728 image_hdr->image_capsule_support);
729
730 printf("--------\n");
731 if (image_hdr->image_capsule_support & CAPSULE_SUPPORT_AUTHENTICATION) {
732 capsule_auth_hdr = (char *)image_hdr + sizeof(*image_hdr);
733 dump_capsule_auth_header(capsule_auth_hdr);
734
735 auth_hdr = capsule_auth_hdr;
736 signature_size = sizeof(auth_hdr->monotonic_count) +
737 auth_hdr->auth_info.hdr.dwLength;
738 fmp_payload_hdr = (char *)capsule_auth_hdr + signature_size;
739 } else {
740 printf("Capsule Authentication Not Enabled\n");
741 fmp_payload_hdr = (char *)image_hdr + sizeof(*image_hdr);
742 }
743
744 fmp_payload_hdr_size = dump_fmp_payload_header(fmp_payload_hdr);
745
746 payload_size = image_hdr->update_image_size - signature_size -
747 fmp_payload_hdr_size;
748 printf("--------\n");
749 printf("Payload Image Size\t\t\t\t: %08X\n", payload_size);
750}
751
752static void dump_fmp_header(
753 struct efi_firmware_management_capsule_header *fmp_hdr)
754{
755 int i;
756 void *capsule_image_hdr;
757
758 printf("EFI_FMP_HDR.VERSION\t\t\t\t: %08X\n", fmp_hdr->version);
759 printf("EFI_FMP_HDR.EMBEDDED_DRIVER_COUNT\t\t: %08X\n",
760 fmp_hdr->embedded_driver_count);
761 printf("EFI_FMP_HDR.PAYLOAD_ITEM_COUNT\t\t\t: %08X\n",
762 fmp_hdr->payload_item_count);
763
764 /*
765 * We currently don't support Embedded Drivers.
766 * Only worry about the payload items.
767 */
768 for (i = 0; i < fmp_hdr->payload_item_count; i++) {
769 capsule_image_hdr = (char *)fmp_hdr +
770 fmp_hdr->item_offset_list[i];
771 dump_fmp_capsule_image_header(capsule_image_hdr);
772 }
773}
774
775static void dump_capsule_header(struct efi_capsule_header *capsule_hdr)
776{
777 printf("EFI_CAPSULE_HDR.CAPSULE_GUID\t\t\t: ");
778 print_guid((void *)&capsule_hdr->capsule_guid);
779 printf("EFI_CAPSULE_HDR.HEADER_SIZE\t\t\t: %08X\n",
780 capsule_hdr->header_size);
781 printf("EFI_CAPSULE_HDR.FLAGS\t\t\t\t: %08X\n", capsule_hdr->flags);
782 printf("EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE\t\t: %08X\n",
783 capsule_hdr->capsule_image_size);
784}
785
786static void normal_capsule_dump(void *capsule_buf)
787{
788 void *fmp_hdr;
789 struct efi_capsule_header *hdr = capsule_buf;
790
791 dump_capsule_header(hdr);
792 printf("--------\n");
793
794 fmp_hdr = (char *)capsule_buf + sizeof(*hdr);
795 dump_fmp_header(fmp_hdr);
796}
797
798static void empty_capsule_dump(void *capsule_buf)
799{
800 efi_guid_t *accept_image_guid;
801 struct efi_capsule_header *hdr = capsule_buf;
802 efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID;
803
804 dump_capsule_header(hdr);
805
806 if (!memcmp(&efi_empty_accept_capsule, &hdr->capsule_guid,
807 sizeof(efi_guid_t))) {
808 accept_image_guid = (void *)(char *)capsule_buf +
809 sizeof(struct efi_capsule_header);
810 printf("--------\n");
811 printf("ACCEPT_IMAGE_GUID\t\t\t\t: ");
812 print_guid(accept_image_guid);
813 }
814}
815
816static void dump_capsule_contents(char *capsule_file)
817{
818 int fd;
819 char *ptr;
820 efi_guid_t efi_fmp_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
821 efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID;
822 efi_guid_t efi_empty_revert_capsule = FW_REVERT_OS_GUID;
823 struct stat sbuf;
824
825 if (!capsule_file) {
826 fprintf(stderr, "No capsule file provided\n");
827 exit(EXIT_FAILURE);
828 }
829
830 if ((fd = open(capsule_file, O_RDONLY)) < 0) {
831 fprintf(stderr, "Error opening capsule file: %s\n",
832 capsule_file);
833 exit(EXIT_FAILURE);
834 }
835
836 if (fstat(fd, &sbuf) < 0) {
837 fprintf(stderr, "Can't stat capsule file: %s\n", capsule_file);
838 exit(EXIT_FAILURE);
839 }
840
841 if ((ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0))
842 == MAP_FAILED) {
843 fprintf(stderr, "Can't mmap capsule file: %s\n", capsule_file);
844 exit(EXIT_FAILURE);
845 }
846
847 if (!memcmp(&efi_fmp_guid, ptr, sizeof(efi_guid_t))) {
848 normal_capsule_dump(ptr);
849 } else if (!memcmp(&efi_empty_accept_capsule, ptr,
850 sizeof(efi_guid_t)) ||
851 !memcmp(&efi_empty_revert_capsule, ptr,
852 sizeof(efi_guid_t))) {
853 empty_capsule_dump(ptr);
854 } else {
855 fprintf(stderr, "Unable to decode the capsule file: %s\n",
856 capsule_file);
857 exit(EXIT_FAILURE);
858 }
859}
860
AKASHI Takahiroba212432022-02-09 19:10:39 +0900861/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900862 * main - main entry function of mkeficapsule
863 * @argc: Number of arguments
864 * @argv: Array of pointers to arguments
865 *
866 * Create an uefi capsule file, optionally signing it.
867 * Parse all the arguments and pass them on to create_fwbin().
868 *
869 * Return:
870 * * 0 - on success
871 * * -1 - on failure
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900872 */
873int main(int argc, char **argv)
874{
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900875 efi_guid_t *guid;
AKASHI Takahiroba212432022-02-09 19:10:39 +0900876 unsigned char uuid_buf[16];
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900877 unsigned long index, instance;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900878 uint64_t mcount;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530879 unsigned long oemflags;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530880 bool capsule_dump;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900881 char *privkey_file, *cert_file;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900882 int c, idx;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900883 struct fmp_payload_header_params fmp_ph_params = { 0 };
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900884
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900885 guid = NULL;
886 index = 0;
887 instance = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900888 mcount = 0;
889 privkey_file = NULL;
890 cert_file = NULL;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530891 capsule_dump = false;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900892 dump_sig = 0;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530893 capsule_type = CAPSULE_NORMAL_BLOB;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530894 oemflags = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900895 for (;;) {
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900896 c = getopt_long(argc, argv, opts_short, options, &idx);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900897 if (c == -1)
898 break;
899
900 switch (c) {
AKASHI Takahiroba212432022-02-09 19:10:39 +0900901 case 'g':
902 if (guid) {
903 fprintf(stderr,
904 "Image type already specified\n");
905 exit(EXIT_FAILURE);
906 }
907 if (uuid_parse(optarg, uuid_buf)) {
908 fprintf(stderr, "Wrong guid format\n");
909 exit(EXIT_FAILURE);
910 }
911 convert_uuid_to_guid(uuid_buf);
912 guid = (efi_guid_t *)uuid_buf;
913 break;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900914 case 'i':
915 index = strtoul(optarg, NULL, 0);
916 break;
917 case 'I':
918 instance = strtoul(optarg, NULL, 0);
919 break;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900920 case 'v':
921 fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
922 fmp_ph_params.have_header = true;
923 break;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900924 case 'p':
925 if (privkey_file) {
926 fprintf(stderr,
927 "Private Key already specified\n");
928 exit(EXIT_FAILURE);
929 }
930 privkey_file = optarg;
931 break;
932 case 'c':
933 if (cert_file) {
934 fprintf(stderr,
935 "Certificate file already specified\n");
936 exit(EXIT_FAILURE);
937 }
938 cert_file = optarg;
939 break;
940 case 'm':
941 mcount = strtoul(optarg, NULL, 0);
942 break;
943 case 'd':
944 dump_sig = 1;
945 break;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530946 case 'A':
947 if (capsule_type) {
948 fprintf(stderr,
949 "Select either of Accept or Revert capsule generation\n");
950 exit(1);
951 }
952 capsule_type = CAPSULE_ACCEPT;
953 break;
954 case 'R':
955 if (capsule_type) {
956 fprintf(stderr,
957 "Select either of Accept or Revert capsule generation\n");
958 exit(1);
959 }
960 capsule_type = CAPSULE_REVERT;
961 break;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530962 case 'o':
963 oemflags = strtoul(optarg, NULL, 0);
964 if (oemflags > 0xffff) {
965 fprintf(stderr,
966 "oemflags must be between 0x0 and 0xffff\n");
967 exit(1);
968 }
969 break;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530970 case 'D':
971 capsule_dump = true;
972 break;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530973 default:
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900974 print_usage();
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900975 exit(EXIT_SUCCESS);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900976 }
977 }
978
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530979 if (capsule_dump) {
980 if (argc != optind + 1) {
981 fprintf(stderr, "Must provide the capsule file to parse\n");
982 exit(EXIT_FAILURE);
983 }
984 dump_capsule_contents(argv[argc - 1]);
985 exit(EXIT_SUCCESS);
986 }
987
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900988 /* check necessary parameters */
Sughosh Ganub39405d2022-10-21 18:16:06 +0530989 if ((capsule_type == CAPSULE_NORMAL_BLOB &&
990 ((argc != optind + 2) || !guid ||
991 ((privkey_file && !cert_file) ||
992 (!privkey_file && cert_file)))) ||
993 (capsule_type != CAPSULE_NORMAL_BLOB &&
994 ((argc != optind + 1) ||
995 ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
996 ((capsule_type == CAPSULE_REVERT) && guid)))) {
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900997 print_usage();
Sughosh Ganu14b44202021-01-22 20:34:56 +0530998 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900999 }
1000
Sughosh Ganub39405d2022-10-21 18:16:06 +05301001 if (capsule_type != CAPSULE_NORMAL_BLOB) {
1002 if (create_empty_capsule(argv[argc - 1], guid,
1003 capsule_type == CAPSULE_ACCEPT) < 0) {
1004 fprintf(stderr, "Creating empty capsule failed\n");
1005 exit(EXIT_FAILURE);
1006 }
1007 } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
Masahisa Kojimade87ca02023-06-07 14:41:56 +09001008 index, instance, &fmp_ph_params, mcount, privkey_file,
Sughosh Ganuc0676382022-10-21 18:16:07 +05301009 cert_file, (uint16_t)oemflags) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +09001010 fprintf(stderr, "Creating firmware capsule failed\n");
Sughosh Ganu14b44202021-01-22 20:34:56 +05301011 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001012 }
1013
Sughosh Ganu14b44202021-01-22 20:34:56 +05301014 exit(EXIT_SUCCESS);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001015}