blob: f28008a0829f6ba18ca26b9d8b7b6ed0256fba69 [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 Takahiro19122aa2020-11-30 18:12:15 +090019
AKASHI Takahiroc246b762022-02-09 19:10:35 +090020#include <gnutls/gnutls.h>
21#include <gnutls/pkcs7.h>
22#include <gnutls/abstract.h>
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090023
Simon Glassb0ef8612024-07-31 08:49:00 -060024#include <version.h>
25
AKASHI Takahiroc246b762022-02-09 19:10:35 +090026#include "eficapsule.h"
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090027
28static const char *tool_name = "mkeficapsule";
29
30efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
AKASHI Takahiroc246b762022-02-09 19:10:35 +090031efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
32
Simon Glassb0ef8612024-07-31 08:49:00 -060033static const char *opts_short = "g:i:I:v:p:c:m:o:dhARDV";
Sughosh Ganub39405d2022-10-21 18:16:06 +053034
35enum {
36 CAPSULE_NORMAL_BLOB = 0,
37 CAPSULE_ACCEPT,
38 CAPSULE_REVERT,
39} capsule_type;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090040
41static struct option options[] = {
AKASHI Takahiroba212432022-02-09 19:10:39 +090042 {"guid", required_argument, NULL, 'g'},
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090043 {"index", required_argument, NULL, 'i'},
44 {"instance", required_argument, NULL, 'I'},
Masahisa Kojimade87ca02023-06-07 14:41:56 +090045 {"fw-version", required_argument, NULL, 'v'},
AKASHI Takahiroc246b762022-02-09 19:10:35 +090046 {"private-key", required_argument, NULL, 'p'},
47 {"certificate", required_argument, NULL, 'c'},
48 {"monotonic-count", required_argument, NULL, 'm'},
49 {"dump-sig", no_argument, NULL, 'd'},
Sughosh Ganub39405d2022-10-21 18:16:06 +053050 {"fw-accept", no_argument, NULL, 'A'},
51 {"fw-revert", no_argument, NULL, 'R'},
Sughosh Ganuc0676382022-10-21 18:16:07 +053052 {"capoemflag", required_argument, NULL, 'o'},
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +053053 {"dump-capsule", no_argument, NULL, 'D'},
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090054 {"help", no_argument, NULL, 'h'},
55 {NULL, 0, NULL, 0},
56};
57
58static void print_usage(void)
59{
AKASHI Takahiroba212432022-02-09 19:10:39 +090060 fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090061 "Options:\n"
Sughosh Ganu079fcf22020-12-30 19:26:59 +053062
AKASHI Takahiroba212432022-02-09 19:10:39 +090063 "\t-g, --guid <guid string> guid for image blob type\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090064 "\t-i, --index <index> update image index\n"
65 "\t-I, --instance <instance> update hardware instance\n"
Masahisa Kojimade87ca02023-06-07 14:41:56 +090066 "\t-v, --fw-version <version> firmware version\n"
AKASHI Takahiroc246b762022-02-09 19:10:35 +090067 "\t-p, --private-key <privkey file> private key file\n"
68 "\t-c, --certificate <cert file> signer's certificate file\n"
69 "\t-m, --monotonic-count <count> monotonic count\n"
70 "\t-d, --dump_sig dump signature (*.p7)\n"
Sughosh Ganub39405d2022-10-21 18:16:06 +053071 "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n"
72 "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n"
Sughosh Ganuc0676382022-10-21 18:16:07 +053073 "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n"
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +053074 "\t-D, --dump-capsule dump the contents of the capsule headers\n"
Simon Glassb0ef8612024-07-31 08:49:00 -060075 "\t-V, --version show version number\n"
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090076 "\t-h, --help print a help message\n",
77 tool_name);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +090078}
79
AKASHI Takahiro30fcea22022-01-18 13:39:45 +090080/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +090081 * auth_context - authentication context
82 * @key_file: Path to a private key file
83 * @cert_file: Path to a certificate file
84 * @image_data: Pointer to firmware data
85 * @image_size: Size of firmware data
86 * @auth: Authentication header
87 * @sig_data: Signature data
88 * @sig_size: Size of signature data
89 *
90 * Data structure used in create_auth_data(). @key_file through
91 * @image_size are input parameters. @auth, @sig_data and @sig_size
92 * are filled in by create_auth_data().
93 */
94struct auth_context {
95 char *key_file;
96 char *cert_file;
97 uint8_t *image_data;
98 size_t image_size;
99 struct efi_firmware_image_authentication auth;
100 uint8_t *sig_data;
101 size_t sig_size;
102};
103
104static int dump_sig;
105
106/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900107 * read_bin_file - read a firmware binary file
108 * @bin: Path to a firmware binary file
109 * @data: Pointer to pointer of allocated buffer
110 * @bin_size: Size of allocated buffer
111 *
112 * Read out a content of binary, @bin, into @data.
113 * A caller should free @data.
114 *
115 * Return:
116 * * 0 - on success
117 * * -1 - on failure
118 */
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900119static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900120{
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900121 FILE *g;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900122 struct stat bin_stat;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900123 void *buf;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900124 size_t size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900125 int ret = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900126
127 g = fopen(bin, "r");
128 if (!g) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900129 fprintf(stderr, "cannot open %s\n", bin);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900130 return -1;
131 }
132 if (stat(bin, &bin_stat) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900133 fprintf(stderr, "cannot determine the size of %s\n", bin);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900134 ret = -1;
135 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900136 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900137 if (bin_stat.st_size > SIZE_MAX) {
138 fprintf(stderr, "file size is too large for malloc: %s\n", bin);
139 ret = -1;
140 goto err;
141 }
142 buf = malloc(bin_stat.st_size);
143 if (!buf) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900144 fprintf(stderr, "cannot allocate memory: %zx\n",
145 (size_t)bin_stat.st_size);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900146 ret = -1;
147 goto err;
148 }
149
150 size = fread(buf, 1, bin_stat.st_size, g);
151 if (size < bin_stat.st_size) {
152 fprintf(stderr, "read failed (%zx)\n", size);
153 ret = -1;
154 goto err;
155 }
156
157 *data = buf;
158 *bin_size = bin_stat.st_size;
159err:
160 fclose(g);
161
162 return ret;
163}
164
165/**
166 * write_capsule_file - write a capsule file
167 * @bin: FILE stream
168 * @data: Pointer to data
169 * @bin_size: Size of data
170 *
171 * Write out data, @data, with the size @bin_size.
172 *
173 * Return:
174 * * 0 - on success
175 * * -1 - on failure
176 */
177static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
178{
179 size_t size_written;
180
181 size_written = fwrite(data, 1, size, f);
182 if (size_written < size) {
183 fprintf(stderr, "%s: write failed (%zx != %zx)\n", msg,
184 size_written, size);
185 return -1;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900186 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900187
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900188 return 0;
189}
190
191/**
192 * create_auth_data - compose authentication data in capsule
193 * @auth_context: Pointer to authentication context
194 *
195 * Fill up an authentication header (.auth) and signature data (.sig_data)
196 * in @auth_context, using library functions from openssl.
197 * All the parameters in @auth_context must be filled in by a caller.
198 *
199 * Return:
200 * * 0 - on success
201 * * -1 - on failure
202 */
203static int create_auth_data(struct auth_context *ctx)
204{
205 gnutls_datum_t cert;
206 gnutls_datum_t key;
207 off_t file_size;
208 gnutls_privkey_t pkey;
209 gnutls_x509_crt_t x509;
210 gnutls_pkcs7_t pkcs7;
211 gnutls_datum_t data;
212 gnutls_datum_t signature;
213 int ret;
214
215 ret = read_bin_file(ctx->cert_file, &cert.data, &file_size);
216 if (ret < 0)
217 return -1;
218 if (file_size > UINT_MAX)
219 return -1;
220 cert.size = file_size;
221
222 ret = read_bin_file(ctx->key_file, &key.data, &file_size);
223 if (ret < 0)
224 return -1;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900225 if (file_size > UINT_MAX)
226 return -1;
227 key.size = file_size;
228
229 /*
230 * For debugging,
231 * gnutls_global_set_time_function(mytime);
232 * gnutls_global_set_log_function(tls_log_func);
233 * gnutls_global_set_log_level(6);
234 */
235
236 ret = gnutls_privkey_init(&pkey);
237 if (ret < 0) {
238 fprintf(stderr, "error in gnutls_privkey_init(): %s\n",
239 gnutls_strerror(ret));
240 return -1;
241 }
242
243 ret = gnutls_x509_crt_init(&x509);
244 if (ret < 0) {
245 fprintf(stderr, "error in gnutls_x509_crt_init(): %s\n",
246 gnutls_strerror(ret));
247 return -1;
248 }
249
250 /* load a private key */
251 ret = gnutls_privkey_import_x509_raw(pkey, &key, GNUTLS_X509_FMT_PEM,
252 0, 0);
253 if (ret < 0) {
254 fprintf(stderr,
255 "error in gnutls_privkey_import_x509_raw(): %s\n",
256 gnutls_strerror(ret));
257 return -1;
258 }
259
260 /* load x509 certificate */
261 ret = gnutls_x509_crt_import(x509, &cert, GNUTLS_X509_FMT_PEM);
262 if (ret < 0) {
263 fprintf(stderr, "error in gnutls_x509_crt_import(): %s\n",
264 gnutls_strerror(ret));
265 return -1;
266 }
267
268 /* generate a PKCS #7 structure */
269 ret = gnutls_pkcs7_init(&pkcs7);
270 if (ret < 0) {
271 fprintf(stderr, "error in gnutls_pkcs7_init(): %s\n",
272 gnutls_strerror(ret));
273 return -1;
274 }
275
276 /* sign */
277 /*
278 * Data should have
279 * * firmware image
280 * * monotonic count
281 * in this order!
282 * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
283 */
284 data.size = ctx->image_size + sizeof(ctx->auth.monotonic_count);
285 data.data = malloc(data.size);
286 if (!data.data) {
287 fprintf(stderr, "allocating memory (0x%x) failed\n", data.size);
288 return -1;
289 }
290 memcpy(data.data, ctx->image_data, ctx->image_size);
291 memcpy(data.data + ctx->image_size, &ctx->auth.monotonic_count,
292 sizeof(ctx->auth.monotonic_count));
293
294 ret = gnutls_pkcs7_sign(pkcs7, x509, pkey, &data, NULL, NULL,
295 GNUTLS_DIG_SHA256,
296 /* GNUTLS_PKCS7_EMBED_DATA? */
297 GNUTLS_PKCS7_INCLUDE_CERT |
298 GNUTLS_PKCS7_INCLUDE_TIME);
299 if (ret < 0) {
300 fprintf(stderr, "error in gnutls_pkcs7)sign(): %s\n",
301 gnutls_strerror(ret));
302 return -1;
303 }
304
305 /* export */
306 ret = gnutls_pkcs7_export2(pkcs7, GNUTLS_X509_FMT_DER, &signature);
307 if (ret < 0) {
308 fprintf(stderr, "error in gnutls_pkcs7_export2: %s\n",
309 gnutls_strerror(ret));
310 return -1;
311 }
312 ctx->sig_data = signature.data;
313 ctx->sig_size = signature.size;
314
315 /* fill auth_info */
316 ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
317 + ctx->sig_size;
318 ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
319 ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
320 memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
321 sizeof(efi_guid_cert_type_pkcs7));
322
323 /*
324 * For better clean-ups,
325 * gnutls_pkcs7_deinit(pkcs7);
326 * gnutls_privkey_deinit(pkey);
327 * gnutls_x509_crt_deinit(x509);
328 * free(cert.data);
329 * free(key.data);
330 * if error
331 * gnutls_free(signature.data);
332 */
333
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900334 return 0;
335}
336
337/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900338 * dump_signature - dump out a signature
339 * @path: Path to a capsule file
340 * @signature: Signature data
341 * @sig_size: Size of signature data
342 *
343 * Signature data pointed to by @signature will be saved into
344 * a file whose file name is @path with ".p7" suffix.
345 *
346 * Return:
347 * * 0 - on success
348 * * -1 - on failure
349 */
350static int dump_signature(const char *path, uint8_t *signature, size_t sig_size)
351{
352 char *sig_path;
353 FILE *f;
354 size_t size;
355 int ret = -1;
356
357 sig_path = malloc(strlen(path) + 3 + 1);
358 if (!sig_path)
359 return ret;
360
361 sprintf(sig_path, "%s.p7", path);
362 f = fopen(sig_path, "w");
363 if (!f)
364 goto err;
365
366 size = fwrite(signature, 1, sig_size, f);
367 if (size == sig_size)
368 ret = 0;
369
370 fclose(f);
371err:
372 free(sig_path);
373 return ret;
374}
375
376/**
377 * free_sig_data - free out signature data
378 * @ctx: Pointer to authentication context
379 *
380 * Free signature data allocated in create_auth_data().
381 */
382static void free_sig_data(struct auth_context *ctx)
383{
384 if (ctx->sig_size)
385 gnutls_free(ctx->sig_data);
386}
387
388/**
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900389 * create_fwbin - create an uefi capsule file
390 * @path: Path to a created capsule file
391 * @bin: Path to a firmware binary to encapsulate
392 * @guid: GUID of related FMP driver
393 * @index: Index number in capsule
394 * @instance: Instance number in capsule
395 * @mcount: Monotonic count in authentication information
396 * @private_file: Path to a private key file
397 * @cert_file: Path to a certificate file
Sughosh Ganuc0676382022-10-21 18:16:07 +0530398 * @oemflags: Capsule OEM Flags, bits 0-15
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900399 *
400 * This function actually does the job of creating an uefi capsule file.
401 * All the arguments must be supplied.
402 * If either @private_file ror @cert_file is NULL, the capsule file
403 * won't be signed.
404 *
405 * Return:
406 * * 0 - on success
407 * * -1 - on failure
408 */
409static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900410 unsigned long index, unsigned long instance,
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900411 struct fmp_payload_header_params *fmp_ph_params,
Sughosh Ganuc0676382022-10-21 18:16:07 +0530412 uint64_t mcount, char *privkey_file, char *cert_file,
413 uint16_t oemflags)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900414{
415 struct efi_capsule_header header;
416 struct efi_firmware_management_capsule_header capsule;
417 struct efi_firmware_management_capsule_image_header image;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900418 struct auth_context auth_context;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900419 FILE *f;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900420 uint8_t *data, *new_data, *buf;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900421 off_t bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900422 uint64_t offset;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900423 int ret;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900424 struct fmp_payload_header payload_header;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900425
426#ifdef DEBUG
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900427 fprintf(stderr, "For output: %s\n", path);
428 fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
429 fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900430#endif
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900431 auth_context.sig_size = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900432 f = NULL;
433 data = NULL;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900434 new_data = NULL;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900435 ret = -1;
436
437 /*
438 * read a firmware binary
439 */
440 if (read_bin_file(bin, &data, &bin_size))
441 goto err;
442
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900443 buf = data;
444
445 /* insert fmp payload header right before the payload */
446 if (fmp_ph_params->have_header) {
447 new_data = malloc(bin_size + sizeof(payload_header));
448 if (!new_data)
449 goto err;
450
451 payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE;
452 payload_header.header_size = sizeof(payload_header);
453 payload_header.fw_version = fmp_ph_params->fw_version;
454 payload_header.lowest_supported_version = 0; /* not used */
455 memcpy(new_data, &payload_header, sizeof(payload_header));
456 memcpy(new_data + sizeof(payload_header), data, bin_size);
457 buf = new_data;
458 bin_size += sizeof(payload_header);
459 }
460
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900461 /* first, calculate signature to determine its size */
462 if (privkey_file && cert_file) {
463 auth_context.key_file = privkey_file;
464 auth_context.cert_file = cert_file;
465 auth_context.auth.monotonic_count = mcount;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900466 auth_context.image_data = buf;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900467 auth_context.image_size = bin_size;
468
469 if (create_auth_data(&auth_context)) {
470 fprintf(stderr, "Signing firmware image failed\n");
471 goto err;
472 }
473
474 if (dump_sig &&
475 dump_signature(path, auth_context.sig_data,
476 auth_context.sig_size)) {
477 fprintf(stderr, "Creating signature file failed\n");
478 goto err;
479 }
480 }
481
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900482 /*
483 * write a capsule file
484 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900485 f = fopen(path, "w");
486 if (!f) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +0900487 fprintf(stderr, "cannot open %s\n", path);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900488 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900489 }
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900490
491 /*
492 * capsule file header
493 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900494 header.capsule_guid = efi_guid_fm_capsule;
495 header.header_size = sizeof(header);
AKASHI Takahiro0f626ce2020-11-30 18:12:16 +0900496 /* TODO: The current implementation ignores flags */
497 header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530498 if (oemflags)
499 header.flags |= oemflags;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900500 header.capsule_image_size = sizeof(header)
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900501 + sizeof(capsule) + sizeof(uint64_t)
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900502 + sizeof(image)
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900503 + bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900504 if (auth_context.sig_size)
505 header.capsule_image_size += sizeof(auth_context.auth)
506 + auth_context.sig_size;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900507 if (write_capsule_file(f, &header, sizeof(header),
508 "Capsule header"))
509 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900510
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900511 /*
512 * firmware capsule header
513 * This capsule has only one firmware capsule image.
514 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900515 capsule.version = 0x00000001;
516 capsule.embedded_driver_count = 0;
517 capsule.payload_item_count = 1;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900518 if (write_capsule_file(f, &capsule, sizeof(capsule),
519 "Firmware capsule header"))
520 goto err;
521
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900522 offset = sizeof(capsule) + sizeof(uint64_t);
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900523 if (write_capsule_file(f, &offset, sizeof(offset),
524 "Offset to capsule image"))
525 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900526
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900527 /*
528 * firmware capsule image header
529 */
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900530 image.version = 0x00000003;
531 memcpy(&image.update_image_type_id, guid, sizeof(*guid));
532 image.update_image_index = index;
AKASHI Takahiroa4c14aa2021-01-22 10:43:49 +0900533 image.reserved[0] = 0;
534 image.reserved[1] = 0;
535 image.reserved[2] = 0;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900536 image.update_image_size = bin_size;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900537 if (auth_context.sig_size)
538 image.update_image_size += sizeof(auth_context.auth)
539 + auth_context.sig_size;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900540 image.update_vendor_code_size = 0; /* none */
541 image.update_hardware_instance = instance;
542 image.image_capsule_support = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900543 if (auth_context.sig_size)
544 image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900545 if (write_capsule_file(f, &image, sizeof(image),
546 "Firmware capsule image header"))
547 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900548
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900549 /*
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900550 * signature
551 */
552 if (auth_context.sig_size) {
553 if (write_capsule_file(f, &auth_context.auth,
554 sizeof(auth_context.auth),
555 "Authentication header"))
556 goto err;
557
558 if (write_capsule_file(f, auth_context.sig_data,
559 auth_context.sig_size, "Signature"))
560 goto err;
561 }
562
563 /*
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900564 * firmware binary
565 */
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900566 if (write_capsule_file(f, buf, bin_size, "Firmware binary"))
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900567 goto err;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900568
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900569 ret = 0;
570err:
571 if (f)
572 fclose(f);
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900573 free_sig_data(&auth_context);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900574 free(data);
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900575 free(new_data);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900576
AKASHI Takahiro30fcea22022-01-18 13:39:45 +0900577 return ret;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900578}
579
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900580/**
AKASHI Takahiroba212432022-02-09 19:10:39 +0900581 * convert_uuid_to_guid() - convert UUID to GUID
582 * @buf: UUID binary
583 *
584 * UUID and GUID have the same data structure, but their binary
585 * formats are different due to the endianness. See lib/uuid.c.
586 * Since uuid_parse() can handle only UUID, this function must
587 * be called to get correct data for GUID when parsing a string.
588 *
589 * The correct data will be returned in @buf.
590 */
591void convert_uuid_to_guid(unsigned char *buf)
592{
593 unsigned char c;
594
595 c = buf[0];
596 buf[0] = buf[3];
597 buf[3] = c;
598 c = buf[1];
599 buf[1] = buf[2];
600 buf[2] = c;
601
602 c = buf[4];
603 buf[4] = buf[5];
604 buf[5] = c;
605
606 c = buf[6];
607 buf[6] = buf[7];
608 buf[7] = c;
609}
610
Sughosh Ganub39405d2022-10-21 18:16:06 +0530611static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept)
612{
613 struct efi_capsule_header header = { 0 };
614 FILE *f = NULL;
615 int ret = -1;
616 efi_guid_t fw_accept_guid = FW_ACCEPT_OS_GUID;
617 efi_guid_t fw_revert_guid = FW_REVERT_OS_GUID;
618 efi_guid_t capsule_guid;
619
620 f = fopen(path, "w");
621 if (!f) {
622 fprintf(stderr, "cannot open %s\n", path);
623 goto err;
624 }
625
626 capsule_guid = fw_accept ? fw_accept_guid : fw_revert_guid;
627
628 memcpy(&header.capsule_guid, &capsule_guid, sizeof(efi_guid_t));
629 header.header_size = sizeof(header);
630 header.flags = 0;
631
632 header.capsule_image_size = fw_accept ?
633 sizeof(header) + sizeof(efi_guid_t) : sizeof(header);
634
635 if (write_capsule_file(f, &header, sizeof(header),
636 "Capsule header"))
637 goto err;
638
639 if (fw_accept) {
640 if (write_capsule_file(f, guid, sizeof(*guid),
641 "FW Accept Capsule Payload"))
642 goto err;
643 }
644
645 ret = 0;
646
647err:
648 if (f)
649 fclose(f);
650
651 return ret;
652}
653
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530654static void print_guid(void *ptr)
655{
656 int i;
657 efi_guid_t *guid = ptr;
658 const uint8_t seq[] = {
659 3, 2, 1, 0, '-', 5, 4, '-', 7, 6,
660 '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 };
661
662 for (i = 0; i < ARRAY_SIZE(seq); i++) {
663 if (seq[i] == '-')
664 putchar(seq[i]);
665 else
666 printf("%02X", guid->b[seq[i]]);
667 }
668
669 printf("\n");
670}
671
672static uint32_t dump_fmp_payload_header(
673 struct fmp_payload_header *fmp_payload_hdr)
674{
675 if (fmp_payload_hdr->signature == FMP_PAYLOAD_HDR_SIGNATURE) {
676 printf("--------\n");
677 printf("FMP_PAYLOAD_HDR.SIGNATURE\t\t\t: %08X\n",
678 FMP_PAYLOAD_HDR_SIGNATURE);
679 printf("FMP_PAYLOAD_HDR.HEADER_SIZE\t\t\t: %08X\n",
680 fmp_payload_hdr->header_size);
681 printf("FMP_PAYLOAD_HDR.FW_VERSION\t\t\t: %08X\n",
682 fmp_payload_hdr->fw_version);
683 printf("FMP_PAYLOAD_HDR.LOWEST_SUPPORTED_VERSION\t: %08X\n",
684 fmp_payload_hdr->lowest_supported_version);
685 return fmp_payload_hdr->header_size;
686 }
687
688 return 0;
689}
690
691static void dump_capsule_auth_header(
692 struct efi_firmware_image_authentication *capsule_auth_hdr)
693{
694 printf("EFI_FIRMWARE_IMAGE_AUTH.MONOTONIC_COUNT\t\t: %08lX\n",
695 capsule_auth_hdr->monotonic_count);
696 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.dwLENGTH\t: %08X\n",
697 capsule_auth_hdr->auth_info.hdr.dwLength);
698 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION\t: %08X\n",
699 capsule_auth_hdr->auth_info.hdr.wRevision);
700 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE\t: %08X\n",
701 capsule_auth_hdr->auth_info.hdr.wCertificateType);
702 printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE\t: ");
703 print_guid(&capsule_auth_hdr->auth_info.cert_type);
704}
705
706static void dump_fmp_capsule_image_header(
707 struct efi_firmware_management_capsule_image_header *image_hdr)
708{
709 void *capsule_auth_hdr;
710 void *fmp_payload_hdr;
711 uint64_t signature_size = 0;
712 uint32_t payload_size = 0;
713 uint32_t fmp_payload_hdr_size = 0;
714 struct efi_firmware_image_authentication *auth_hdr;
715
716 printf("--------\n");
717 printf("FMP_CAPSULE_IMAGE_HDR.VERSION\t\t\t: %08X\n",
718 image_hdr->version);
719 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID\t: ");
720 print_guid(&image_hdr->update_image_type_id);
721 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX\t: %08X\n",
722 image_hdr->update_image_index);
723 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_SIZE\t\t: %08X\n",
724 image_hdr->update_image_size);
725 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_VENDOR_CODE_SIZE\t: %08X\n",
726 image_hdr->update_vendor_code_size);
727 printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_HARDWARE_INSTANCE\t: %08lX\n",
728 image_hdr->update_hardware_instance);
729 printf("FMP_CAPSULE_IMAGE_HDR.IMAGE_CAPSULE_SUPPORT\t: %08lX\n",
730 image_hdr->image_capsule_support);
731
732 printf("--------\n");
733 if (image_hdr->image_capsule_support & CAPSULE_SUPPORT_AUTHENTICATION) {
734 capsule_auth_hdr = (char *)image_hdr + sizeof(*image_hdr);
735 dump_capsule_auth_header(capsule_auth_hdr);
736
737 auth_hdr = capsule_auth_hdr;
738 signature_size = sizeof(auth_hdr->monotonic_count) +
739 auth_hdr->auth_info.hdr.dwLength;
740 fmp_payload_hdr = (char *)capsule_auth_hdr + signature_size;
741 } else {
742 printf("Capsule Authentication Not Enabled\n");
743 fmp_payload_hdr = (char *)image_hdr + sizeof(*image_hdr);
744 }
745
746 fmp_payload_hdr_size = dump_fmp_payload_header(fmp_payload_hdr);
747
748 payload_size = image_hdr->update_image_size - signature_size -
749 fmp_payload_hdr_size;
750 printf("--------\n");
751 printf("Payload Image Size\t\t\t\t: %08X\n", payload_size);
752}
753
754static void dump_fmp_header(
755 struct efi_firmware_management_capsule_header *fmp_hdr)
756{
757 int i;
758 void *capsule_image_hdr;
759
760 printf("EFI_FMP_HDR.VERSION\t\t\t\t: %08X\n", fmp_hdr->version);
761 printf("EFI_FMP_HDR.EMBEDDED_DRIVER_COUNT\t\t: %08X\n",
762 fmp_hdr->embedded_driver_count);
763 printf("EFI_FMP_HDR.PAYLOAD_ITEM_COUNT\t\t\t: %08X\n",
764 fmp_hdr->payload_item_count);
765
766 /*
767 * We currently don't support Embedded Drivers.
768 * Only worry about the payload items.
769 */
770 for (i = 0; i < fmp_hdr->payload_item_count; i++) {
771 capsule_image_hdr = (char *)fmp_hdr +
772 fmp_hdr->item_offset_list[i];
773 dump_fmp_capsule_image_header(capsule_image_hdr);
774 }
775}
776
777static void dump_capsule_header(struct efi_capsule_header *capsule_hdr)
778{
779 printf("EFI_CAPSULE_HDR.CAPSULE_GUID\t\t\t: ");
780 print_guid((void *)&capsule_hdr->capsule_guid);
781 printf("EFI_CAPSULE_HDR.HEADER_SIZE\t\t\t: %08X\n",
782 capsule_hdr->header_size);
783 printf("EFI_CAPSULE_HDR.FLAGS\t\t\t\t: %08X\n", capsule_hdr->flags);
784 printf("EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE\t\t: %08X\n",
785 capsule_hdr->capsule_image_size);
786}
787
788static void normal_capsule_dump(void *capsule_buf)
789{
790 void *fmp_hdr;
791 struct efi_capsule_header *hdr = capsule_buf;
792
793 dump_capsule_header(hdr);
794 printf("--------\n");
795
796 fmp_hdr = (char *)capsule_buf + sizeof(*hdr);
797 dump_fmp_header(fmp_hdr);
798}
799
800static void empty_capsule_dump(void *capsule_buf)
801{
802 efi_guid_t *accept_image_guid;
803 struct efi_capsule_header *hdr = capsule_buf;
804 efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID;
805
806 dump_capsule_header(hdr);
807
808 if (!memcmp(&efi_empty_accept_capsule, &hdr->capsule_guid,
809 sizeof(efi_guid_t))) {
810 accept_image_guid = (void *)(char *)capsule_buf +
811 sizeof(struct efi_capsule_header);
812 printf("--------\n");
813 printf("ACCEPT_IMAGE_GUID\t\t\t\t: ");
814 print_guid(accept_image_guid);
815 }
816}
817
818static void dump_capsule_contents(char *capsule_file)
819{
820 int fd;
821 char *ptr;
822 efi_guid_t efi_fmp_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
823 efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID;
824 efi_guid_t efi_empty_revert_capsule = FW_REVERT_OS_GUID;
825 struct stat sbuf;
826
827 if (!capsule_file) {
828 fprintf(stderr, "No capsule file provided\n");
829 exit(EXIT_FAILURE);
830 }
831
832 if ((fd = open(capsule_file, O_RDONLY)) < 0) {
833 fprintf(stderr, "Error opening capsule file: %s\n",
834 capsule_file);
835 exit(EXIT_FAILURE);
836 }
837
838 if (fstat(fd, &sbuf) < 0) {
839 fprintf(stderr, "Can't stat capsule file: %s\n", capsule_file);
840 exit(EXIT_FAILURE);
841 }
842
843 if ((ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0))
844 == MAP_FAILED) {
845 fprintf(stderr, "Can't mmap capsule file: %s\n", capsule_file);
846 exit(EXIT_FAILURE);
847 }
848
849 if (!memcmp(&efi_fmp_guid, ptr, sizeof(efi_guid_t))) {
850 normal_capsule_dump(ptr);
851 } else if (!memcmp(&efi_empty_accept_capsule, ptr,
852 sizeof(efi_guid_t)) ||
853 !memcmp(&efi_empty_revert_capsule, ptr,
854 sizeof(efi_guid_t))) {
855 empty_capsule_dump(ptr);
856 } else {
857 fprintf(stderr, "Unable to decode the capsule file: %s\n",
858 capsule_file);
859 exit(EXIT_FAILURE);
860 }
861}
862
AKASHI Takahiroba212432022-02-09 19:10:39 +0900863/**
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900864 * main - main entry function of mkeficapsule
865 * @argc: Number of arguments
866 * @argv: Array of pointers to arguments
867 *
868 * Create an uefi capsule file, optionally signing it.
869 * Parse all the arguments and pass them on to create_fwbin().
870 *
871 * Return:
872 * * 0 - on success
873 * * -1 - on failure
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900874 */
875int main(int argc, char **argv)
876{
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900877 efi_guid_t *guid;
AKASHI Takahiroba212432022-02-09 19:10:39 +0900878 unsigned char uuid_buf[16];
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900879 unsigned long index, instance;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900880 uint64_t mcount;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530881 unsigned long oemflags;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530882 bool capsule_dump;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900883 char *privkey_file, *cert_file;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900884 int c, idx;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900885 struct fmp_payload_header_params fmp_ph_params = { 0 };
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900886
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900887 guid = NULL;
888 index = 0;
889 instance = 0;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900890 mcount = 0;
891 privkey_file = NULL;
892 cert_file = NULL;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530893 capsule_dump = false;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900894 dump_sig = 0;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530895 capsule_type = CAPSULE_NORMAL_BLOB;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530896 oemflags = 0;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900897 for (;;) {
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900898 c = getopt_long(argc, argv, opts_short, options, &idx);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900899 if (c == -1)
900 break;
901
902 switch (c) {
AKASHI Takahiroba212432022-02-09 19:10:39 +0900903 case 'g':
904 if (guid) {
905 fprintf(stderr,
906 "Image type already specified\n");
907 exit(EXIT_FAILURE);
908 }
909 if (uuid_parse(optarg, uuid_buf)) {
910 fprintf(stderr, "Wrong guid format\n");
911 exit(EXIT_FAILURE);
912 }
913 convert_uuid_to_guid(uuid_buf);
914 guid = (efi_guid_t *)uuid_buf;
915 break;
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900916 case 'i':
917 index = strtoul(optarg, NULL, 0);
918 break;
919 case 'I':
920 instance = strtoul(optarg, NULL, 0);
921 break;
Masahisa Kojimade87ca02023-06-07 14:41:56 +0900922 case 'v':
923 fmp_ph_params.fw_version = strtoul(optarg, NULL, 0);
924 fmp_ph_params.have_header = true;
925 break;
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900926 case 'p':
927 if (privkey_file) {
928 fprintf(stderr,
929 "Private Key already specified\n");
930 exit(EXIT_FAILURE);
931 }
932 privkey_file = optarg;
933 break;
934 case 'c':
935 if (cert_file) {
936 fprintf(stderr,
937 "Certificate file already specified\n");
938 exit(EXIT_FAILURE);
939 }
940 cert_file = optarg;
941 break;
942 case 'm':
943 mcount = strtoul(optarg, NULL, 0);
944 break;
945 case 'd':
946 dump_sig = 1;
947 break;
Sughosh Ganub39405d2022-10-21 18:16:06 +0530948 case 'A':
949 if (capsule_type) {
950 fprintf(stderr,
951 "Select either of Accept or Revert capsule generation\n");
952 exit(1);
953 }
954 capsule_type = CAPSULE_ACCEPT;
955 break;
956 case 'R':
957 if (capsule_type) {
958 fprintf(stderr,
959 "Select either of Accept or Revert capsule generation\n");
960 exit(1);
961 }
962 capsule_type = CAPSULE_REVERT;
963 break;
Sughosh Ganuc0676382022-10-21 18:16:07 +0530964 case 'o':
965 oemflags = strtoul(optarg, NULL, 0);
966 if (oemflags > 0xffff) {
967 fprintf(stderr,
968 "oemflags must be between 0x0 and 0xffff\n");
969 exit(1);
970 }
971 break;
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530972 case 'D':
973 capsule_dump = true;
974 break;
Simon Glassb0ef8612024-07-31 08:49:00 -0600975 case 'V':
976 printf("mkeficapsule version %s\n", PLAIN_VERSION);
977 exit(EXIT_SUCCESS);
Sughosh Ganub39405d2022-10-21 18:16:06 +0530978 default:
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900979 print_usage();
Simon Glass55f22b02024-07-31 08:49:03 -0600980 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +0900981 }
982 }
983
Sughosh Ganua7d1fdc2023-10-10 14:40:54 +0530984 if (capsule_dump) {
985 if (argc != optind + 1) {
986 fprintf(stderr, "Must provide the capsule file to parse\n");
987 exit(EXIT_FAILURE);
988 }
989 dump_capsule_contents(argv[argc - 1]);
990 exit(EXIT_SUCCESS);
991 }
992
AKASHI Takahiroc246b762022-02-09 19:10:35 +0900993 /* check necessary parameters */
Sughosh Ganub39405d2022-10-21 18:16:06 +0530994 if ((capsule_type == CAPSULE_NORMAL_BLOB &&
995 ((argc != optind + 2) || !guid ||
996 ((privkey_file && !cert_file) ||
997 (!privkey_file && cert_file)))) ||
998 (capsule_type != CAPSULE_NORMAL_BLOB &&
999 ((argc != optind + 1) ||
1000 ((capsule_type == CAPSULE_ACCEPT) && !guid) ||
1001 ((capsule_type == CAPSULE_REVERT) && guid)))) {
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001002 print_usage();
Sughosh Ganu14b44202021-01-22 20:34:56 +05301003 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001004 }
1005
Sughosh Ganub39405d2022-10-21 18:16:06 +05301006 if (capsule_type != CAPSULE_NORMAL_BLOB) {
1007 if (create_empty_capsule(argv[argc - 1], guid,
1008 capsule_type == CAPSULE_ACCEPT) < 0) {
1009 fprintf(stderr, "Creating empty capsule failed\n");
1010 exit(EXIT_FAILURE);
1011 }
1012 } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid,
Masahisa Kojimade87ca02023-06-07 14:41:56 +09001013 index, instance, &fmp_ph_params, mcount, privkey_file,
Sughosh Ganuc0676382022-10-21 18:16:07 +05301014 cert_file, (uint16_t)oemflags) < 0) {
AKASHI Takahiro6cf40d42022-01-18 13:39:44 +09001015 fprintf(stderr, "Creating firmware capsule failed\n");
Sughosh Ganu14b44202021-01-22 20:34:56 +05301016 exit(EXIT_FAILURE);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001017 }
1018
Sughosh Ganu14b44202021-01-22 20:34:56 +05301019 exit(EXIT_SUCCESS);
AKASHI Takahiro19122aa2020-11-30 18:12:15 +09001020}