uboot/tools/mkeficapsule.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2018 Linaro Limited
   4 *              Author: AKASHI Takahiro
   5 */
   6
   7#include <errno.h>
   8#include <getopt.h>
   9#include <malloc.h>
  10#include <stdbool.h>
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <string.h>
  14#include <unistd.h>
  15#include <linux/types.h>
  16
  17#include <sys/mman.h>
  18#include <sys/stat.h>
  19#include <sys/types.h>
  20
  21#include "fdt_host.h"
  22
  23typedef __u8 u8;
  24typedef __u16 u16;
  25typedef __u32 u32;
  26typedef __u64 u64;
  27typedef __s16 s16;
  28typedef __s32 s32;
  29
  30#define aligned_u64 __aligned_u64
  31
  32#define SIGNATURE_NODENAME      "signature"
  33#define OVERLAY_NODENAME        "__overlay__"
  34
  35#ifndef __packed
  36#define __packed __attribute__((packed))
  37#endif
  38
  39#include <efi.h>
  40#include <efi_api.h>
  41
  42static const char *tool_name = "mkeficapsule";
  43
  44efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
  45efi_guid_t efi_guid_image_type_uboot_fit =
  46                EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
  47efi_guid_t efi_guid_image_type_uboot_raw =
  48                EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
  49
  50static struct option options[] = {
  51        {"fit", required_argument, NULL, 'f'},
  52        {"raw", required_argument, NULL, 'r'},
  53        {"index", required_argument, NULL, 'i'},
  54        {"instance", required_argument, NULL, 'I'},
  55        {"dtb", required_argument, NULL, 'D'},
  56        {"public key", required_argument, NULL, 'K'},
  57        {"overlay", no_argument, NULL, 'O'},
  58        {"help", no_argument, NULL, 'h'},
  59        {NULL, 0, NULL, 0},
  60};
  61
  62static void print_usage(void)
  63{
  64        printf("Usage: %s [options] <output file>\n"
  65               "Options:\n"
  66
  67               "\t-f, --fit <fit image>       new FIT image file\n"
  68               "\t-r, --raw <raw image>       new raw image file\n"
  69               "\t-i, --index <index>         update image index\n"
  70               "\t-I, --instance <instance>   update hardware instance\n"
  71               "\t-K, --public-key <key file> public key esl file\n"
  72               "\t-D, --dtb <dtb file>        dtb file\n"
  73               "\t-O, --overlay               the dtb file is an overlay\n"
  74               "\t-h, --help                  print a help message\n",
  75               tool_name);
  76}
  77
  78static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
  79                                bool overlay)
  80{
  81        int parent;
  82        int ov_node;
  83        int frag_node;
  84        int ret = 0;
  85
  86        if (overlay) {
  87                /*
  88                 * The signature would be stored in the
  89                 * first fragment node of the overlay
  90                 */
  91                frag_node = fdt_first_subnode(dptr, 0);
  92                if (frag_node == -FDT_ERR_NOTFOUND) {
  93                        fprintf(stderr,
  94                                "Couldn't find the fragment node: %s\n",
  95                                fdt_strerror(frag_node));
  96                        goto done;
  97                }
  98
  99                ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
 100                if (ov_node == -FDT_ERR_NOTFOUND) {
 101                        fprintf(stderr,
 102                                "Couldn't find the __overlay__ node: %s\n",
 103                                fdt_strerror(ov_node));
 104                        goto done;
 105                }
 106        } else {
 107                ov_node = 0;
 108        }
 109
 110        parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
 111        if (parent == -FDT_ERR_NOTFOUND) {
 112                parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
 113                if (parent < 0) {
 114                        ret = parent;
 115                        if (ret != -FDT_ERR_NOSPACE) {
 116                                fprintf(stderr,
 117                                        "Couldn't create signature node: %s\n",
 118                                        fdt_strerror(parent));
 119                        }
 120                }
 121        }
 122        if (ret)
 123                goto done;
 124
 125        /* Write the key to the FDT node */
 126        ret = fdt_setprop(dptr, parent, "capsule-key",
 127                          sptr, key_size);
 128
 129done:
 130        if (ret)
 131                ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
 132
 133        return ret;
 134}
 135
 136static int add_public_key(const char *pkey_file, const char *dtb_file,
 137                          bool overlay)
 138{
 139        int ret;
 140        int srcfd = -1;
 141        int destfd = -1;
 142        void *sptr = NULL;
 143        void *dptr = NULL;
 144        off_t src_size;
 145        struct stat pub_key;
 146        struct stat dtb;
 147
 148        /* Find out the size of the public key */
 149        srcfd = open(pkey_file, O_RDONLY);
 150        if (srcfd == -1) {
 151                fprintf(stderr, "%s: Can't open %s: %s\n",
 152                        __func__, pkey_file, strerror(errno));
 153                ret = -1;
 154                goto err;
 155        }
 156
 157        ret = fstat(srcfd, &pub_key);
 158        if (ret == -1) {
 159                fprintf(stderr, "%s: Can't stat %s: %s\n",
 160                        __func__, pkey_file, strerror(errno));
 161                ret = -1;
 162                goto err;
 163        }
 164
 165        src_size = pub_key.st_size;
 166
 167        /* mmap the public key esl file */
 168        sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
 169        if (sptr == MAP_FAILED) {
 170                fprintf(stderr, "%s: Failed to mmap %s:%s\n",
 171                        __func__, pkey_file, strerror(errno));
 172                ret = -1;
 173                goto err;
 174        }
 175
 176        /* Open the dest FDT */
 177        destfd = open(dtb_file, O_RDWR);
 178        if (destfd == -1) {
 179                fprintf(stderr, "%s: Can't open %s: %s\n",
 180                        __func__, dtb_file, strerror(errno));
 181                ret = -1;
 182                goto err;
 183        }
 184
 185        ret = fstat(destfd, &dtb);
 186        if (ret == -1) {
 187                fprintf(stderr, "%s: Can't stat %s: %s\n",
 188                        __func__, dtb_file, strerror(errno));
 189                goto err;
 190        }
 191
 192        dtb.st_size += src_size + 0x30;
 193        if (ftruncate(destfd, dtb.st_size)) {
 194                fprintf(stderr, "%s: Can't expand %s: %s\n",
 195                        __func__, dtb_file, strerror(errno));
 196                ret = -1;
 197                goto err;
 198        }
 199
 200        errno = 0;
 201        /* mmap the dtb file */
 202        dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
 203                    destfd, 0);
 204        if (dptr == MAP_FAILED) {
 205                fprintf(stderr, "%s: Failed to mmap %s:%s\n",
 206                        __func__, dtb_file, strerror(errno));
 207                ret = -1;
 208                goto err;
 209        }
 210
 211        if (fdt_check_header(dptr)) {
 212                fprintf(stderr, "%s: Invalid FDT header\n", __func__);
 213                ret = -1;
 214                goto err;
 215        }
 216
 217        ret = fdt_open_into(dptr, dptr, dtb.st_size);
 218        if (ret) {
 219                fprintf(stderr, "%s: Cannot expand FDT: %s\n",
 220                        __func__, fdt_strerror(ret));
 221                ret = -1;
 222                goto err;
 223        }
 224
 225        /* Copy the esl file to the expanded FDT */
 226        ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
 227        if (ret < 0) {
 228                fprintf(stderr, "%s: Unable to add public key to the FDT\n",
 229                        __func__);
 230                ret = -1;
 231                goto err;
 232        }
 233
 234        ret = 0;
 235
 236err:
 237        if (sptr)
 238                munmap(sptr, src_size);
 239
 240        if (dptr)
 241                munmap(dptr, dtb.st_size);
 242
 243        if (srcfd != -1)
 244                close(srcfd);
 245
 246        if (destfd != -1)
 247                close(destfd);
 248
 249        return ret;
 250}
 251
 252static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 253                        unsigned long index, unsigned long instance)
 254{
 255        struct efi_capsule_header header;
 256        struct efi_firmware_management_capsule_header capsule;
 257        struct efi_firmware_management_capsule_image_header image;
 258        FILE *f, *g;
 259        struct stat bin_stat;
 260        u8 *data;
 261        size_t size;
 262        u64 offset;
 263
 264#ifdef DEBUG
 265        printf("For output: %s\n", path);
 266        printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
 267        printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
 268#endif
 269
 270        g = fopen(bin, "r");
 271        if (!g) {
 272                printf("cannot open %s\n", bin);
 273                return -1;
 274        }
 275        if (stat(bin, &bin_stat) < 0) {
 276                printf("cannot determine the size of %s\n", bin);
 277                goto err_1;
 278        }
 279        data = malloc(bin_stat.st_size);
 280        if (!data) {
 281                printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
 282                goto err_1;
 283        }
 284        f = fopen(path, "w");
 285        if (!f) {
 286                printf("cannot open %s\n", path);
 287                goto err_2;
 288        }
 289        header.capsule_guid = efi_guid_fm_capsule;
 290        header.header_size = sizeof(header);
 291        /* TODO: The current implementation ignores flags */
 292        header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
 293        header.capsule_image_size = sizeof(header)
 294                                        + sizeof(capsule) + sizeof(u64)
 295                                        + sizeof(image)
 296                                        + bin_stat.st_size;
 297
 298        size = fwrite(&header, 1, sizeof(header), f);
 299        if (size < sizeof(header)) {
 300                printf("write failed (%zx)\n", size);
 301                goto err_3;
 302        }
 303
 304        capsule.version = 0x00000001;
 305        capsule.embedded_driver_count = 0;
 306        capsule.payload_item_count = 1;
 307        size = fwrite(&capsule, 1, sizeof(capsule), f);
 308        if (size < (sizeof(capsule))) {
 309                printf("write failed (%zx)\n", size);
 310                goto err_3;
 311        }
 312        offset = sizeof(capsule) + sizeof(u64);
 313        size = fwrite(&offset, 1, sizeof(offset), f);
 314        if (size < sizeof(offset)) {
 315                printf("write failed (%zx)\n", size);
 316                goto err_3;
 317        }
 318
 319        image.version = 0x00000003;
 320        memcpy(&image.update_image_type_id, guid, sizeof(*guid));
 321        image.update_image_index = index;
 322        image.reserved[0] = 0;
 323        image.reserved[1] = 0;
 324        image.reserved[2] = 0;
 325        image.update_image_size = bin_stat.st_size;
 326        image.update_vendor_code_size = 0; /* none */
 327        image.update_hardware_instance = instance;
 328        image.image_capsule_support = 0;
 329
 330        size = fwrite(&image, 1, sizeof(image), f);
 331        if (size < sizeof(image)) {
 332                printf("write failed (%zx)\n", size);
 333                goto err_3;
 334        }
 335        size = fread(data, 1, bin_stat.st_size, g);
 336        if (size < bin_stat.st_size) {
 337                printf("read failed (%zx)\n", size);
 338                goto err_3;
 339        }
 340        size = fwrite(data, 1, bin_stat.st_size, f);
 341        if (size < bin_stat.st_size) {
 342                printf("write failed (%zx)\n", size);
 343                goto err_3;
 344        }
 345
 346        fclose(f);
 347        fclose(g);
 348        free(data);
 349
 350        return 0;
 351
 352err_3:
 353        fclose(f);
 354err_2:
 355        free(data);
 356err_1:
 357        fclose(g);
 358
 359        return -1;
 360}
 361
 362/*
 363 * Usage:
 364 *   $ mkeficapsule -f <firmware binary> <output file>
 365 */
 366int main(int argc, char **argv)
 367{
 368        char *file;
 369        char *pkey_file;
 370        char *dtb_file;
 371        efi_guid_t *guid;
 372        unsigned long index, instance;
 373        int c, idx;
 374        int ret;
 375        bool overlay = false;
 376
 377        file = NULL;
 378        pkey_file = NULL;
 379        dtb_file = NULL;
 380        guid = NULL;
 381        index = 0;
 382        instance = 0;
 383        for (;;) {
 384                c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
 385                if (c == -1)
 386                        break;
 387
 388                switch (c) {
 389                case 'f':
 390                        if (file) {
 391                                printf("Image already specified\n");
 392                                return -1;
 393                        }
 394                        file = optarg;
 395                        guid = &efi_guid_image_type_uboot_fit;
 396                        break;
 397                case 'r':
 398                        if (file) {
 399                                printf("Image already specified\n");
 400                                return -1;
 401                        }
 402                        file = optarg;
 403                        guid = &efi_guid_image_type_uboot_raw;
 404                        break;
 405                case 'i':
 406                        index = strtoul(optarg, NULL, 0);
 407                        break;
 408                case 'I':
 409                        instance = strtoul(optarg, NULL, 0);
 410                        break;
 411                case 'K':
 412                        if (pkey_file) {
 413                                printf("Public Key already specified\n");
 414                                return -1;
 415                        }
 416                        pkey_file = optarg;
 417                        break;
 418                case 'D':
 419                        if (dtb_file) {
 420                                printf("DTB file already specified\n");
 421                                return -1;
 422                        }
 423                        dtb_file = optarg;
 424                        break;
 425                case 'O':
 426                        overlay = true;
 427                        break;
 428                case 'h':
 429                        print_usage();
 430                        return 0;
 431                }
 432        }
 433
 434        /* need a fit image file or raw image file */
 435        if (!file && !pkey_file && !dtb_file) {
 436                print_usage();
 437                exit(EXIT_FAILURE);
 438        }
 439
 440        if (pkey_file && dtb_file) {
 441                ret = add_public_key(pkey_file, dtb_file, overlay);
 442                if (ret == -1) {
 443                        printf("Adding public key to the dtb failed\n");
 444                        exit(EXIT_FAILURE);
 445                } else {
 446                        exit(EXIT_SUCCESS);
 447                }
 448        }
 449
 450        if (create_fwbin(argv[optind], file, guid, index, instance)
 451                        < 0) {
 452                printf("Creating firmware capsule failed\n");
 453                exit(EXIT_FAILURE);
 454        }
 455
 456        exit(EXIT_SUCCESS);
 457}
 458