blob: 52be1f122eecd41f24ed11d2755a612da5dd6fb6 [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 Ganuc0676382022-10-21 18:16:07 +053032static const char *opts_short = "g:i:I:v:p:c:m:o:dhAR";
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'},
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090052 {"help", no_argument, NULL, 'h'},
53 {NULL, 0, NULL, 0},
54};
55
56static void print_usage(void)
57{
AKASHI Takahiroba212432022-02-09 19:10:39 +090058 fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090059 "Options:\n"
Sughosh Ganu079fcf22020-12-30 19:26:59 +053060
AKASHI Takahiroba212432022-02-09 19:10:39 +090061 "\t-g, --guid <guid string> guid for image blob type\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090062 "\t-i, --index <index> update image index\n"
63 "\t-I, --instance <instance> update hardware instance\n"
Masahisa Kojimade87ca02023-06-07 14:41:56 +090064 "\t-v, --fw-version <version> firmware version\n"
AKASHI Takahiroc246b762022-02-09 19:10:35 +090065 "\t-p, --private-key <privkey file> private key file\n"
66 "\t-c, --certificate <cert file> signer's certificate file\n"
67 "\t-m, --monotonic-count <count> monotonic count\n"
68 "\t-d, --dump_sig dump signature (*.p7)\n"
Sughosh Ganub39405d2022-10-21 18:16:06 +053069 "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n"
70 "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n"
Sughosh Ganuc0676382022-10-21 18:16:07 +053071 "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090072 "\t-h, --help print a help message\n",
73 tool_name);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090074}
75
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090076/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +090077 * auth_context - authentication context
78 * @key_file: Path to a private key file
79 * @cert_file: Path to a certificate file
80 * @image_data: Pointer to firmware data
81 * @image_size: Size of firmware data
82 * @auth: Authentication header
83 * @sig_data: Signature data
84 * @sig_size: Size of signature data
85 *
86 * Data structure used in create_auth_data(). @key_file through
87 * @image_size are input parameters. @auth, @sig_data and @sig_size
88 * are filled in by create_auth_data().
89 */
90struct auth_context {
91 char *key_file;
92 char *cert_file;
93 uint8_t *image_data;
94 size_t image_size;
95 struct efi_firmware_image_authentication auth;
96 uint8_t *sig_data;
97 size_t sig_size;
98};
99
100static int dump_sig;
101
102/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900103 * read_bin_file - read a firmware binary file
104 * @bin: Path to a firmware binary file
105 * @data: Pointer to pointer of allocated buffer
106 * @bin_size: Size of allocated buffer
107 *
108 * Read out a content of binary, @bin, into @data.
109 * A caller should free @data.
110 *
111 * Return:
112 * * 0 - on success
113 * * -1 - on failure
114 */
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900115static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900116{
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900117 FILE *g;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900118 struct stat bin_stat;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900119 void *buf;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900120 size_t size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900121 int ret = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900122
123 g = fopen(bin, "r");
124 if (!g) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900125 fprintf(stderr, "cannot open %s\n", bin);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900126 return -1;
127 }
128 if (stat(bin, &bin_stat) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900129 fprintf(stderr, "cannot determine the size of %s\n", bin);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900130 ret = -1;
131 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900132 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900133 if (bin_stat.st_size > SIZE_MAX) {
134 fprintf(stderr, "file size is too large for malloc: %s\n", bin);
135 ret = -1;
136 goto err;
137 }
138 buf = malloc(bin_stat.st_size);
139 if (!buf) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900140 fprintf(stderr, "cannot allocate memory: %zx\n",
141 (size_t)bin_stat.st_size);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900142 ret = -1;
143 goto err;
144 }
145
146 size = fread(buf, 1, bin_stat.st_size, g);
147 if (size < bin_stat.st_size) {
148 fprintf(stderr, "read failed (%zx)\n", size);
149 ret = -1;
150 goto err;
151 }
152
153 *data = buf;
154 *bin_size = bin_stat.st_size;
155err:
156 fclose(g);
157
158 return ret;
159}
160
161/**
162 * write_capsule_file - write a capsule file
163 * @bin: FILE stream
164 * @data: Pointer to data
165 * @bin_size: Size of data
166 *
167 * Write out data, @data, with the size @bin_size.
168 *
169 * Return:
170 * * 0 - on success
171 * * -1 - on failure
172 */
173static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
174{
175 size_t size_written;
176
177 size_written = fwrite(data, 1, size, f);
178 if (size_written < size) {
179 fprintf(stderr, "%s: write failed (%zx != %zx)\n", msg,
180 size_written, size);
181 return -1;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900182 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900183
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900184 return 0;
185}
186
187/**
188 * create_auth_data - compose authentication data in capsule
189 * @auth_context: Pointer to authentication context
190 *
191 * Fill up an authentication header (.auth) and signature data (.sig_data)
192 * in @auth_context, using library functions from openssl.
193 * All the parameters in @auth_context must be filled in by a caller.
194 *
195 * Return:
196 * * 0 - on success
197 * * -1 - on failure
198 */
199static int create_auth_data(struct auth_context *ctx)
200{
201 gnutls_datum_t cert;
202 gnutls_datum_t key;
203 off_t file_size;
204 gnutls_privkey_t pkey;
205 gnutls_x509_crt_t x509;
206 gnutls_pkcs7_t pkcs7;
207 gnutls_datum_t data;
208 gnutls_datum_t signature;
209 int ret;
210
211 ret = read_bin_file(ctx->cert_file, &cert.data, &file_size);
212 if (ret < 0)
213 return -1;
214 if (file_size > UINT_MAX)
215 return -1;
216 cert.size = file_size;
217
218 ret = read_bin_file(ctx->key_file, &key.data, &file_size);
219 if (ret < 0)
220 return -1;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900221 if (file_size > UINT_MAX)
222 return -1;
223 key.size = file_size;
224
225 /*
226 * For debugging,
227 * gnutls_global_set_time_function(mytime);
228 * gnutls_global_set_log_function(tls_log_func);
229 * gnutls_global_set_log_level(6);
230 */
231
232 ret = gnutls_privkey_init(&pkey);
233 if (ret < 0) {
234 fprintf(stderr, "error in gnutls_privkey_init(): %s\n",
235 gnutls_strerror(ret));
236 return -1;
237 }
238
239 ret = gnutls_x509_crt_init(&x509);
240 if (ret < 0) {
241 fprintf(stderr, "error in gnutls_x509_crt_init(): %s\n",
242 gnutls_strerror(ret));
243 return -1;
244 }
245
246 /* load a private key */
247 ret = gnutls_privkey_import_x509_raw(pkey, &key, GNUTLS_X509_FMT_PEM,
248 0, 0);
249 if (ret < 0) {
250 fprintf(stderr,
251 "error in gnutls_privkey_import_x509_raw(): %s\n",
252 gnutls_strerror(ret));
253 return -1;
254 }
255
256 /* load x509 certificate */
257 ret = gnutls_x509_crt_import(x509, &cert, GNUTLS_X509_FMT_PEM);
258 if (ret < 0) {
259 fprintf(stderr, "error in gnutls_x509_crt_import(): %s\n",
260 gnutls_strerror(ret));
261 return -1;
262 }
263
264 /* generate a PKCS #7 structure */
265 ret = gnutls_pkcs7_init(&pkcs7);
266 if (ret < 0) {
267 fprintf(stderr, "error in gnutls_pkcs7_init(): %s\n",
268 gnutls_strerror(ret));
269 return -1;
270 }
271
272 /* sign */
273 /*
274 * Data should have
275 * * firmware image
276 * * monotonic count
277 * in this order!
278 * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
279 */
280 data.size = ctx->image_size + sizeof(ctx->auth.monotonic_count);
281 data.data = malloc(data.size);
282 if (!data.data) {
283 fprintf(stderr, "allocating memory (0x%x) failed\n", data.size);
284 return -1;
285 }
286 memcpy(data.data, ctx->image_data, ctx->image_size);
287 memcpy(data.data + ctx->image_size, &ctx->auth.monotonic_count,
288 sizeof(ctx->auth.monotonic_count));
289
290 ret = gnutls_pkcs7_sign(pkcs7, x509, pkey, &data, NULL, NULL,
291 GNUTLS_DIG_SHA256,
292 /* GNUTLS_PKCS7_EMBED_DATA? */
293 GNUTLS_PKCS7_INCLUDE_CERT |
294 GNUTLS_PKCS7_INCLUDE_TIME);
295 if (ret < 0) {
296 fprintf(stderr, "error in gnutls_pkcs7)sign(): %s\n",
297 gnutls_strerror(ret));
298 return -1;
299 }
300
301 /* export */
302 ret = gnutls_pkcs7_export2(pkcs7, GNUTLS_X509_FMT_DER, &signature);
303 if (ret < 0) {
304 fprintf(stderr, "error in gnutls_pkcs7_export2: %s\n",
305 gnutls_strerror(ret));
306 return -1;
307 }
308 ctx->sig_data = signature.data;
309 ctx->sig_size = signature.size;
310
311 /* fill auth_info */
312 ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
313 + ctx->sig_size;
314 ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
315 ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
316 memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
317 sizeof(efi_guid_cert_type_pkcs7));
318
319 /*
320 * For better clean-ups,
321 * gnutls_pkcs7_deinit(pkcs7);
322 * gnutls_privkey_deinit(pkey);
323 * gnutls_x509_crt_deinit(x509);
324 * free(cert.data);
325 * free(key.data);
326 * if error
327 * gnutls_free(signature.data);
328 */
329
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900330 return 0;
331}
332
333/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900334 * dump_signature - dump out a signature
335 * @path: Path to a capsule file
336 * @signature: Signature data
337 * @sig_size: Size of signature data
338 *
339 * Signature data pointed to by @signature will be saved into
340 * a file whose file name is @path with ".p7" suffix.
341 *
342 * Return:
343 * * 0 - on success
344 * * -1 - on failure
345 */
346static int dump_signature(const char *path, uint8_t *signature, size_t sig_size)
347{
348 char *sig_path;
349 FILE *f;
350 size_t size;
351 int ret = -1;
352
353 sig_path = malloc(strlen(path) + 3 + 1);
354 if (!sig_path)
355 return ret;
356
357 sprintf(sig_path, "%s.p7", path);
358 f = fopen(sig_path, "w");
359 if (!f)
360 goto err;
361
362 size = fwrite(signature, 1, sig_size, f);
363 if (size == sig_size)
364 ret = 0;
365
366 fclose(f);
367err:
368 free(sig_path);
369 return ret;
370}
371
372/**
373 * free_sig_data - free out signature data
374 * @ctx: Pointer to authentication context
375 *
376 * Free signature data allocated in create_auth_data().
377 */
378static void free_sig_data(struct auth_context *ctx)
379{
380 if (ctx->sig_size)
381 gnutls_free(ctx->sig_data);
382}
383
384/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900385 * create_fwbin - create an uefi capsule file
386 * @path: Path to a created capsule file
387 * @bin: Path to a firmware binary to encapsulate
388 * @guid: GUID of related FMP driver
389 * @index: Index number in capsule
390 * @instance: Instance number in capsule
391 * @mcount: Monotonic count in authentication information
392 * @private_file: Path to a private key file
393 * @cert_file: Path to a certificate file
Sughosh Ganuc0676382022-10-21 18:16:07 +0530394 * @oemflags: Capsule OEM Flags, bits 0-15
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900395 *
396 * This function actually does the job of creating an uefi capsule file.
397 * All the arguments must be supplied.
398 * If either @private_file ror @cert_file is NULL, the capsule file
399 * won't be signed.
400 *
401 * Return:
402 * * 0 - on success
403 * * -1 - on failure
404 */
405static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900406 unsigned long index, unsigned long instance,
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900407 struct fmp_payload_header_params *fmp_ph_params,
Sughosh Ganuc0676382022-10-21 18:16:07 +0530408 uint64_t mcount, char *privkey_file, char *cert_file,
409 uint16_t oemflags)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900410{
411 struct efi_capsule_header header;
412 struct efi_firmware_management_capsule_header capsule;
413 struct efi_firmware_management_capsule_image_header image;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900414 struct auth_context auth_context;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900415 FILE *f;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900416 uint8_t *data, *new_data, *buf;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900417 off_t bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900418 uint64_t offset;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900419 int ret;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900420 struct fmp_payload_header payload_header;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900421
422#ifdef DEBUG
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900423 fprintf(stderr, "For output: %s\n", path);
424 fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
425 fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900426#endif
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900427 auth_context.sig_size = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900428 f = NULL;
429 data = NULL;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900430 new_data = NULL;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900431 ret = -1;
432
433 /*
434 * read a firmware binary
435 */
436 if (read_bin_file(bin, &data, &bin_size))
437 goto err;
438
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900439 buf = data;
440
441 /* insert fmp payload header right before the payload */
442 if (fmp_ph_params->have_header) {
443 new_data = malloc(bin_size + sizeof(payload_header));
444 if (!new_data)
445 goto err;
446
447 payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
448 payload_header.header_size = sizeof(payload_header);
449 payload_header.fw_version = fmp_ph_params->fw_version;
450 payload_header.lowest_supported_version = 0; /* not used */
451 memcpy(new_data, &payload_header, sizeof(payload_header));
452 memcpy(new_data + sizeof(payload_header), data, bin_size);
453 buf = new_data;
454 bin_size += sizeof(payload_header);
455 }
456
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900457 /* first, calculate signature to determine its size */
458 if (privkey_file && cert_file) {
459 auth_context.key_file = privkey_file;
460 auth_context.cert_file = cert_file;
461 auth_context.auth.monotonic_count = mcount;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900462 auth_context.image_data = buf;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900463 auth_context.image_size = bin_size;
464
465 if (create_auth_data(&auth_context)) {
466 fprintf(stderr, "Signing firmware image failed\n");
467 goto err;
468 }
469
470 if (dump_sig &&
471 dump_signature(path, auth_context.sig_data,
472 auth_context.sig_size)) {
473 fprintf(stderr, "Creating signature file failed\n");
474 goto err;
475 }
476 }
477
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900478 /*
479 * write a capsule file
480 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900481 f = fopen(path, "w");
482 if (!f) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900483 fprintf(stderr, "cannot open %s\n", path);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900484 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900485 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900486
487 /*
488 * capsule file header
489 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900490 header.capsule_guid = efi_guid_fm_capsule;
491 header.header_size = sizeof(header);
AKASHI Takahiro0f626ce2020-11-30 18:12:16 +0900492 /* TODO: The current implementation ignores flags */
493 header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530494 if (oemflags)
495 header.flags |= oemflags;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900496 header.capsule_image_size = sizeof(header)
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900497 + sizeof(capsule) + sizeof(uint64_t)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900498 + sizeof(image)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900499 + bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900500 if (auth_context.sig_size)
501 header.capsule_image_size += sizeof(auth_context.auth)
502 + auth_context.sig_size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900503 if (write_capsule_file(f, &header, sizeof(header),
504 "Capsule header"))
505 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900506
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900507 /*
508 * firmware capsule header
509 * This capsule has only one firmware capsule image.
510 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900511 capsule.version = 0x00000001;
512 capsule.embedded_driver_count = 0;
513 capsule.payload_item_count = 1;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900514 if (write_capsule_file(f, &capsule, sizeof(capsule),
515 "Firmware capsule header"))
516 goto err;
517
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900518 offset = sizeof(capsule) + sizeof(uint64_t);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900519 if (write_capsule_file(f, &offset, sizeof(offset),
520 "Offset to capsule image"))
521 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900522
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900523 /*
524 * firmware capsule image header
525 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900526 image.version = 0x00000003;
527 memcpy(&image.update_image_type_id, guid, sizeof(*guid));
528 image.update_image_index = index;
AKASHI Takahiroa4c14aa2021-01-22 10:43:49 +0900529 image.reserved[0] = 0;
530 image.reserved[1] = 0;
531 image.reserved[2] = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900532 image.update_image_size = bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900533 if (auth_context.sig_size)
534 image.update_image_size += sizeof(auth_context.auth)
535 + auth_context.sig_size;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900536 image.update_vendor_code_size = 0; /* none */
537 image.update_hardware_instance = instance;
538 image.image_capsule_support = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900539 if (auth_context.sig_size)
540 image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900541 if (write_capsule_file(f, &image, sizeof(image),
542 "Firmware capsule image header"))
543 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900544
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900545 /*
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900546 * signature
547 */
548 if (auth_context.sig_size) {
549 if (write_capsule_file(f, &auth_context.auth,
550 sizeof(auth_context.auth),
551 "Authentication header"))
552 goto err;
553
554 if (write_capsule_file(f, auth_context.sig_data,
555 auth_context.sig_size, "Signature"))
556 goto err;
557 }
558
559 /*
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900560 * firmware binary
561 */
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900562 if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900563 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900564
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900565 ret = 0;
566err:
567 if (f)
568 fclose(f);
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900569 free_sig_data(&auth_context);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900570 free(data);
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900571 free(new_data);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900572
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900573 return ret;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900574}
575
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900576/**
AKASHI Takahiroba212432022-02-09 19:10:39 +0900577 * convert_uuid_to_guid() - convert UUID to GUID
578 * @buf: UUID binary
579 *
580 * UUID and GUID have the same data structure, but their binary
581 * formats are different due to the endianness. See lib/uuid.c.
582 * Since uuid_parse() can handle only UUID, this function must
583 * be called to get correct data for GUID when parsing a string.
584 *
585 * The correct data will be returned in @buf.
586 */
587void convert_uuid_to_guid(unsigned char *buf)
588{
589 unsigned char c;
590
591 c = buf[0];
592 buf[0] = buf[3];
593 buf[3] = c;
594 c = buf[1];
595 buf[1] = buf[2];
596 buf[2] = c;
597
598 c = buf[4];
599 buf[4] = buf[5];
600 buf[5] = c;
601
602 c = buf[6];
603 buf[6] = buf[7];
604 buf[7] = c;
605}
606
Sughosh Ganub39405d2022-10-21 18:16:06 +0530607static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept)
608{
609 struct efi_capsule_header header = { 0 };
610 FILE *f = NULL;
611 int ret = -1;
612 efi_guid_t fw_accept_guid = FW_ACCEPT_OS_GUID;
613 efi_guid_t fw_revert_guid = FW_REVERT_OS_GUID;
614 efi_guid_t capsule_guid;
615
616 f = fopen(path, "w");
617 if (!f) {
618 fprintf(stderr, "cannot open %s\n", path);
619 goto err;
620 }
621
622 capsule_guid = fw_accept ? fw_accept_guid : fw_revert_guid;
623
624 memcpy(&header.capsule_guid, &capsule_guid, sizeof(efi_guid_t));
625 header.header_size = sizeof(header);
626 header.flags = 0;
627
628 header.capsule_image_size = fw_accept ?
629 sizeof(header) + sizeof(efi_guid_t) : sizeof(header);
630
631 if (write_capsule_file(f, &header, sizeof(header),
632 "Capsule header"))
633 goto err;
634
635 if (fw_accept) {
636 if (write_capsule_file(f, guid, sizeof(*guid),
637 "FW Accept Capsule Payload"))
638 goto err;
639 }
640
641 ret = 0;
642
643err:
644 if (f)
645 fclose(f);
646
647 return ret;
648}
649
AKASHI Takahiroba212432022-02-09 19:10:39 +0900650/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900651 * main - main entry function of mkeficapsule
652 * @argc: Number of arguments
653 * @argv: Array of pointers to arguments
654 *
655 * Create an uefi capsule file, optionally signing it.
656 * Parse all the arguments and pass them on to create_fwbin().
657 *
658 * Return:
659 * * 0 - on success
660 * * -1 - on failure
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900661 */
662int main(int argc, char **argv)
663{
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900664 efi_guid_t *guid;
AKASHI Takahiroba212432022-02-09 19:10:39 +0900665 unsigned char uuid_buf[16];
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900666 unsigned long index, instance;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900667 uint64_t mcount;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530668 unsigned long oemflags;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900669 char *privkey_file, *cert_file;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900670 int c, idx;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900671 struct fmp_payload_header_params fmp_ph_params = { 0 };
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900672
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900673 guid = NULL;
674 index = 0;
675 instance = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900676 mcount = 0;
677 privkey_file = NULL;
678 cert_file = NULL;
679 dump_sig = 0;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530680 capsule_type = CAPSULE_NORMAL_BLOB;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530681 oemflags = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900682 for (;;) {
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900683 c = getopt_long(argc, argv, opts_short, options, &idx);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900684 if (c == -1)
685 break;
686
687 switch (c) {
AKASHI Takahiroba212432022-02-09 19:10:39 +0900688 case 'g':
689 if (guid) {
690 fprintf(stderr,
691 "Image type already specified\n");
692 exit(EXIT_FAILURE);
693 }
694 if (uuid_parse(optarg, uuid_buf)) {
695 fprintf(stderr, "Wrong guid format\n");
696 exit(EXIT_FAILURE);
697 }
698 convert_uuid_to_guid(uuid_buf);
699 guid = (efi_guid_t *)uuid_buf;
700 break;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900701 case 'i':
702 index = strtoul(optarg, NULL, 0);
703 break;
704 case 'I':
705 instance = strtoul(optarg, NULL, 0);
706 break;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900707 case 'v':
708 fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
709 fmp_ph_params.have_header = true;
710 break;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900711 case 'p':
712 if (privkey_file) {
713 fprintf(stderr,
714 "Private Key already specified\n");
715 exit(EXIT_FAILURE);
716 }
717 privkey_file = optarg;
718 break;
719 case 'c':
720 if (cert_file) {
721 fprintf(stderr,
722 "Certificate file already specified\n");
723 exit(EXIT_FAILURE);
724 }
725 cert_file = optarg;
726 break;
727 case 'm':
728 mcount = strtoul(optarg, NULL, 0);
729 break;
730 case 'd':
731 dump_sig = 1;
732 break;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530733 case 'A':
734 if (capsule_type) {
735 fprintf(stderr,
736 "Select either of Accept or Revert capsule generation\n");
737 exit(1);
738 }
739 capsule_type = CAPSULE_ACCEPT;
740 break;
741 case 'R':
742 if (capsule_type) {
743 fprintf(stderr,
744 "Select either of Accept or Revert capsule generation\n");
745 exit(1);
746 }
747 capsule_type = CAPSULE_REVERT;
748 break;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530749 case 'o':
750 oemflags = strtoul(optarg, NULL, 0);
751 if (oemflags > 0xffff) {
752 fprintf(stderr,
753 "oemflags must be between 0x0 and 0xffff\n");
754 exit(1);
755 }
756 break;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530757 default:
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900758 print_usage();
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900759 exit(EXIT_SUCCESS);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900760 }
761 }
762
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900763 /* check necessary parameters */
Sughosh Ganub39405d2022-10-21 18:16:06 +0530764 if ((capsule_type == CAPSULE_NORMAL_BLOB &&
765 ((argc != optind + 2) || !guid ||
766 ((privkey_file && !cert_file) ||
767 (!privkey_file && cert_file)))) ||
768 (capsule_type != CAPSULE_NORMAL_BLOB &&
769 ((argc != optind + 1) ||
770 ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
771 ((capsule_type == CAPSULE_REVERT) && guid)))) {
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900772 print_usage();
Sughosh Ganu14b44202021-01-22 20:34:56 +0530773 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900774 }
775
Sughosh Ganub39405d2022-10-21 18:16:06 +0530776 if (capsule_type != CAPSULE_NORMAL_BLOB) {
777 if (create_empty_capsule(argv[argc - 1], guid,
778 capsule_type == CAPSULE_ACCEPT) < 0) {
779 fprintf(stderr, "Creating empty capsule failed\n");
780 exit(EXIT_FAILURE);
781 }
782 } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900783 index, instance, &fmp_ph_params, mcount, privkey_file,
Sughosh Ganuc0676382022-10-21 18:16:07 +0530784 cert_file, (uint16_t)oemflags) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900785 fprintf(stderr, "Creating firmware capsule failed\n");
Sughosh Ganu14b44202021-01-22 20:34:56 +0530786 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900787 }
788
Sughosh Ganu14b44202021-01-22 20:34:56 +0530789 exit(EXIT_SUCCESS);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900790}