uboot/tools/zynqmpimage.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Michal Simek <michals@xilinx.com>
   4 * Copyright (C) 2015 Nathan Rossi <nathan@nathanrossi.com>
   5 *
   6 * The following Boot Header format/structures and values are defined in the
   7 * following documents:
   8 *   * ug1085 ZynqMP TRM doc v1.4 (Chapter 11, Table 11-4)
   9 *   * ug1137 ZynqMP Software Developer Guide v6.0 (Chapter 16)
  10 *
  11 * Expected Header Size = 0x9C0
  12 * Forced as 'little' endian, 32-bit words
  13 *
  14 *  0x  0 - Interrupt table (8 words)
  15 *  ...     (Default value = 0xeafffffe)
  16 *  0x 1f
  17 *  0x 20 - Width detection
  18 *         * DEFAULT_WIDTHDETECTION    0xaa995566
  19 *  0x 24 - Image identifier
  20 *         * DEFAULT_IMAGEIDENTIFIER   0x584c4e58
  21 *  0x 28 - Encryption
  22 *         * 0x00000000 - None
  23 *         * 0xa5c3c5a3 - eFuse
  24 *         * 0xa5c3c5a7 - obfuscated key in eFUSE
  25 *         * 0x3a5c3c5a - bbRam
  26 *         * 0xa35c7ca5 - obfuscated key in boot header
  27 *  0x 2C - Image load
  28 *  0x 30 - Image offset
  29 *  0x 34 - PFW image length
  30 *  0x 38 - Total PFW image length
  31 *  0x 3C - Image length
  32 *  0x 40 - Total image length
  33 *  0x 44 - Image attributes
  34 *  0x 48 - Header checksum
  35 *  0x 4c - Obfuscated key
  36 *  ...
  37 *  0x 68
  38 *  0x 6c - Reserved
  39 *  0x 70 - User defined
  40 *  ...
  41 *  0x 9c
  42 *  0x a0 - Secure header initialization vector
  43 *  ...
  44 *  0x a8
  45 *  0x ac - Obfuscated key initialization vector
  46 *  ...
  47 *  0x b4
  48 *  0x b8 - Register Initialization, 511 Address and Data word pairs
  49 *         * List is terminated with an address of 0xffffffff or
  50 *  ...    * at the max number of entries
  51 *  0x8b4
  52 *  0x8b8 - Reserved
  53 *  ...
  54 *  0x9bf
  55 *  0x9c0 - Data/Image starts here or above
  56 */
  57
  58#include "imagetool.h"
  59#include "mkimage.h"
  60#include "zynqmpimage.h"
  61#include <image.h>
  62
  63static struct zynqmp_header zynqmpimage_header;
  64static void *dynamic_header;
  65static FILE *fpmu;
  66
  67static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr)
  68{
  69        uint32_t checksum = 0;
  70
  71        if (ptr == NULL)
  72                return 0;
  73
  74        checksum += le32_to_cpu(ptr->width_detection);
  75        checksum += le32_to_cpu(ptr->image_identifier);
  76        checksum += le32_to_cpu(ptr->encryption);
  77        checksum += le32_to_cpu(ptr->image_load);
  78        checksum += le32_to_cpu(ptr->image_offset);
  79        checksum += le32_to_cpu(ptr->pfw_image_length);
  80        checksum += le32_to_cpu(ptr->total_pfw_image_length);
  81        checksum += le32_to_cpu(ptr->image_size);
  82        checksum += le32_to_cpu(ptr->image_stored_size);
  83        checksum += le32_to_cpu(ptr->image_attributes);
  84        checksum = ~checksum;
  85
  86        return cpu_to_le32(checksum);
  87}
  88
  89void zynqmpimage_default_header(struct zynqmp_header *ptr)
  90{
  91        int i;
  92
  93        if (ptr == NULL)
  94                return;
  95
  96        ptr->width_detection = HEADER_WIDTHDETECTION;
  97        ptr->image_attributes = HEADER_CPU_SELECT_A53_64BIT;
  98        ptr->image_identifier = HEADER_IMAGEIDENTIFIER;
  99        ptr->encryption = cpu_to_le32(ENCRYPTION_NONE);
 100
 101        /* Setup not-supported/constant/reserved fields */
 102        for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++)
 103                ptr->interrupt_vectors[i] = HEADER_INTERRUPT_DEFAULT;
 104
 105        for (i = 0; i < HEADER_REGINITS; i++) {
 106                ptr->register_init[i].address = HEADER_REGINIT_NULL;
 107                ptr->register_init[i].data = 0;
 108        }
 109
 110        /*
 111         * Certain reserved fields are required to be set to 0, ensure they are
 112         * set as such.
 113         */
 114        ptr->pfw_image_length = 0x0;
 115        ptr->total_pfw_image_length = 0x0;
 116}
 117
 118/* mkimage glue functions */
 119static int zynqmpimage_verify_header(unsigned char *ptr, int image_size,
 120                struct image_tool_params *params)
 121{
 122        struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
 123
 124        if (image_size < sizeof(struct zynqmp_header))
 125                return -1;
 126
 127        if (zynqhdr->width_detection != HEADER_WIDTHDETECTION)
 128                return -1;
 129        if (zynqhdr->image_identifier != HEADER_IMAGEIDENTIFIER)
 130                return -1;
 131
 132        if (zynqmpimage_checksum(zynqhdr) != zynqhdr->checksum)
 133                return -1;
 134
 135        return 0;
 136}
 137
 138static void print_partition(const void *ptr, const struct partition_header *ph)
 139{
 140        uint32_t attr = le32_to_cpu(ph->attributes);
 141        unsigned long len = le32_to_cpu(ph->len) * 4;
 142        const char *part_owner;
 143        const char *dest_devs[0x8] = {
 144                "none", "PS", "PL", "PMU", "unknown", "unknown", "unknown",
 145                "unknown"
 146        };
 147
 148        switch (attr & PART_ATTR_PART_OWNER_MASK) {
 149        case PART_ATTR_PART_OWNER_FSBL:
 150                part_owner = "FSBL";
 151                break;
 152        case PART_ATTR_PART_OWNER_UBOOT:
 153                part_owner = "U-Boot";
 154                break;
 155        default:
 156                part_owner = "Unknown";
 157                break;
 158        }
 159
 160        printf("%s payload on CPU %s (%s):\n", part_owner,
 161               dest_cpus[(attr & PART_ATTR_DEST_CPU_MASK) >> 8],
 162               dest_devs[(attr & PART_ATTR_DEST_DEVICE_MASK) >> 4]);
 163
 164        printf("    Offset     : 0x%08x\n", le32_to_cpu(ph->offset) * 4);
 165        printf("    Size       : %lu (0x%lx) bytes\n", len, len);
 166        printf("    Load       : 0x%08llx",
 167               (unsigned long long)le64_to_cpu(ph->load_address));
 168        if (ph->load_address != ph->entry_point)
 169                printf(" (entry=0x%08llx)\n",
 170                       (unsigned long long)le64_to_cpu(ph->entry_point));
 171        else
 172                printf("\n");
 173        printf("    Attributes : ");
 174
 175        if (attr & PART_ATTR_VEC_LOCATION)
 176                printf("vec ");
 177
 178        if (attr & PART_ATTR_ENCRYPTED)
 179                printf("encrypted ");
 180
 181        switch (attr & PART_ATTR_CHECKSUM_MASK) {
 182        case PART_ATTR_CHECKSUM_MD5:
 183                printf("md5 ");
 184                break;
 185        case PART_ATTR_CHECKSUM_SHA2:
 186                printf("sha2 ");
 187                break;
 188        case PART_ATTR_CHECKSUM_SHA3:
 189                printf("sha3 ");
 190                break;
 191        }
 192
 193        if (attr & PART_ATTR_BIG_ENDIAN)
 194                printf("BigEndian ");
 195
 196        if (attr & PART_ATTR_RSA_SIG)
 197                printf("RSA ");
 198
 199        if (attr & PART_ATTR_A53_EXEC_AARCH32)
 200                printf("AArch32 ");
 201
 202        if (attr & PART_ATTR_TARGET_EL_MASK)
 203                printf("EL%d ", (attr & PART_ATTR_TARGET_EL_MASK) >> 1);
 204
 205        if (attr & PART_ATTR_TZ_SECURE)
 206                printf("secure ");
 207        printf("\n");
 208
 209        printf("    Checksum   : 0x%08x\n", le32_to_cpu(ph->checksum));
 210}
 211
 212void zynqmpimage_print_header(const void *ptr)
 213{
 214        struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
 215        int i;
 216
 217        printf("Image Type   : Xilinx ZynqMP Boot Image support\n");
 218        printf("Image Offset : 0x%08x\n", le32_to_cpu(zynqhdr->image_offset));
 219        printf("Image Size   : %lu bytes (%lu bytes packed)\n",
 220               (unsigned long)le32_to_cpu(zynqhdr->image_size),
 221               (unsigned long)le32_to_cpu(zynqhdr->image_stored_size));
 222
 223        if (zynqhdr->pfw_image_length)
 224                printf("PMUFW Size   : %lu bytes (%lu bytes packed)\n",
 225                       (unsigned long)le32_to_cpu(zynqhdr->pfw_image_length),
 226                       (unsigned long)le32_to_cpu(
 227                                zynqhdr->total_pfw_image_length));
 228
 229        printf("Image Load   : 0x%08x\n", le32_to_cpu(zynqhdr->image_load));
 230        printf("Checksum     : 0x%08x\n", le32_to_cpu(zynqhdr->checksum));
 231
 232        for (i = 0; i < HEADER_INTERRUPT_VECTORS; i++) {
 233                if (zynqhdr->interrupt_vectors[i] == HEADER_INTERRUPT_DEFAULT)
 234                        continue;
 235
 236                printf("Modified Interrupt Vector Address [%d]: 0x%08x\n", i,
 237                       le32_to_cpu(zynqhdr->interrupt_vectors[i]));
 238        }
 239
 240        for (i = 0; i < HEADER_REGINITS; i++) {
 241                if (zynqhdr->register_init[i].address == HEADER_REGINIT_NULL)
 242                        break;
 243
 244                if (i == 0)
 245                        printf("Custom Register Initialization:\n");
 246
 247                printf("    @ 0x%08x -> 0x%08x\n",
 248                       le32_to_cpu(zynqhdr->register_init[i].address),
 249                       le32_to_cpu(zynqhdr->register_init[i].data));
 250        }
 251
 252        if (zynqhdr->image_header_table_offset) {
 253                struct image_header_table *iht = (void *)ptr +
 254                        zynqhdr->image_header_table_offset;
 255                struct partition_header *ph;
 256                uint32_t ph_offset;
 257                uint32_t next;
 258                int i;
 259
 260                ph_offset = le32_to_cpu(iht->partition_header_offset) * 4;
 261                ph = (void *)ptr + ph_offset;
 262                for (i = 0; i < le32_to_cpu(iht->nr_parts); i++) {
 263                        next = le32_to_cpu(ph->next_partition_offset) * 4;
 264
 265                        /* Partition 0 is the base image itself */
 266                        if (i)
 267                                print_partition(ptr, ph);
 268
 269                        ph = (void *)ptr + next;
 270                }
 271        }
 272
 273        free(dynamic_header);
 274}
 275
 276static int zynqmpimage_check_params(struct image_tool_params *params)
 277{
 278        if (!params)
 279                return 0;
 280
 281        if (params->addr != 0x0) {
 282                fprintf(stderr, "Error: Load Address cannot be specified.\n");
 283                return -1;
 284        }
 285
 286        /*
 287         * If the entry point is specified ensure it is 64 byte aligned.
 288         */
 289        if (params->eflag && (params->ep % 64 != 0)) {
 290                fprintf(stderr,
 291                        "Error: Entry Point must be aligned to a 64-byte boundary.\n");
 292                return -1;
 293        }
 294
 295        return !(params->lflag || params->dflag);
 296}
 297
 298static int zynqmpimage_check_image_types(uint8_t type)
 299{
 300        if (type == IH_TYPE_ZYNQMPIMAGE)
 301                return EXIT_SUCCESS;
 302        return EXIT_FAILURE;
 303}
 304
 305static uint32_t fsize(FILE *fp)
 306{
 307        int size, ret, origin;
 308
 309        origin = ftell(fp);
 310        if (origin < 0) {
 311                fprintf(stderr, "Incorrect file size\n");
 312                fclose(fp);
 313                exit(2);
 314        }
 315
 316        ret = fseek(fp, 0L, SEEK_END);
 317        if (ret) {
 318                fprintf(stderr, "Incorrect file SEEK_END\n");
 319                fclose(fp);
 320                exit(3);
 321        }
 322
 323        size = ftell(fp);
 324        if (size < 0) {
 325                fprintf(stderr, "Incorrect file size\n");
 326                fclose(fp);
 327                exit(4);
 328        }
 329
 330        /* going back */
 331        ret = fseek(fp, origin, SEEK_SET);
 332        if (ret) {
 333                fprintf(stderr, "Incorrect file SEEK_SET to %d\n", origin);
 334                fclose(fp);
 335                exit(3);
 336        }
 337
 338        return size;
 339}
 340
 341static void zynqmpimage_pmufw(struct zynqmp_header *zynqhdr,
 342                              const char *filename)
 343{
 344        uint32_t size;
 345
 346        /* Setup PMU fw size */
 347        zynqhdr->pfw_image_length = fsize(fpmu);
 348        zynqhdr->total_pfw_image_length = zynqhdr->pfw_image_length;
 349
 350        zynqhdr->image_size -= zynqhdr->pfw_image_length;
 351        zynqhdr->image_stored_size -= zynqhdr->total_pfw_image_length;
 352
 353        /* Read the whole PMUFW to the header */
 354        size = fread(&zynqhdr->__reserved4[66], 1,
 355                     zynqhdr->pfw_image_length, fpmu);
 356        if (size != zynqhdr->pfw_image_length) {
 357                fprintf(stderr, "Cannot read PMUFW file: %s\n", filename);
 358                fclose(fpmu);
 359                exit(1);
 360        }
 361
 362        fclose(fpmu);
 363}
 364
 365static void zynqmpimage_parse_initparams(struct zynqmp_header *zynqhdr,
 366        const char *filename)
 367{
 368        FILE *fp;
 369        struct zynqmp_reginit reginit;
 370        unsigned int reg_count = 0;
 371        int r, err;
 372        struct stat path_stat;
 373
 374        /* Expect a table of register-value pairs, e.g. "0x12345678 0x4321" */
 375        fp = fopen(filename, "r");
 376        if (!fp) {
 377                fprintf(stderr, "Cannot open initparams file: %s\n", filename);
 378                exit(1);
 379        }
 380
 381        err = fstat(fileno(fp), &path_stat);
 382        if (err) {
 383                fclose(fp);
 384                return;
 385        }
 386
 387        if (!S_ISREG(path_stat.st_mode)) {
 388                fclose(fp);
 389                return;
 390        }
 391
 392        do {
 393                r = fscanf(fp, "%x %x", &reginit.address, &reginit.data);
 394                if (r == 2) {
 395                        zynqhdr->register_init[reg_count] = reginit;
 396                        ++reg_count;
 397                }
 398                r = fscanf(fp, "%*[^\n]\n"); /* Skip to next line */
 399        } while ((r != EOF) && (reg_count < HEADER_REGINITS));
 400        fclose(fp);
 401}
 402
 403static void zynqmpimage_set_header(void *ptr, struct stat *sbuf, int ifd,
 404                struct image_tool_params *params)
 405{
 406        struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
 407        zynqmpimage_default_header(zynqhdr);
 408
 409        /* place image directly after header */
 410        zynqhdr->image_offset =
 411                cpu_to_le32((uint32_t)sizeof(struct zynqmp_header));
 412        zynqhdr->image_size = cpu_to_le32(params->file_size -
 413                                          sizeof(struct zynqmp_header));
 414        zynqhdr->image_stored_size = zynqhdr->image_size;
 415        zynqhdr->image_load = 0xfffc0000;
 416        if (params->eflag)
 417                zynqhdr->image_load = cpu_to_le32((uint32_t)params->ep);
 418
 419        /* PMUFW */
 420        if (fpmu)
 421                zynqmpimage_pmufw(zynqhdr, params->imagename);
 422
 423        /* User can pass in text file with init list */
 424        if (strlen(params->imagename2))
 425                zynqmpimage_parse_initparams(zynqhdr, params->imagename2);
 426
 427        zynqhdr->checksum = zynqmpimage_checksum(zynqhdr);
 428}
 429
 430static int zynqmpimage_vrec_header(struct image_tool_params *params,
 431                                   struct image_type_params *tparams)
 432{
 433        struct stat path_stat;
 434        char *filename = params->imagename;
 435        int err;
 436
 437        /* Handle static case without PMUFW */
 438        tparams->header_size = sizeof(struct zynqmp_header);
 439        tparams->hdr = (void *)&zynqmpimage_header;
 440
 441        /* PMUFW name is passed via params->imagename */
 442        if (strlen(filename) == 0)
 443                return EXIT_SUCCESS;
 444
 445        fpmu = fopen(filename, "r");
 446        if (!fpmu) {
 447                fprintf(stderr, "Cannot open PMUFW file: %s\n", filename);
 448                return EXIT_FAILURE;
 449        }
 450
 451        err = fstat(fileno(fpmu), &path_stat);
 452        if (err) {
 453                fclose(fpmu);
 454                fpmu = NULL;
 455                return EXIT_FAILURE;
 456        }
 457
 458        if (!S_ISREG(path_stat.st_mode)) {
 459                fclose(fpmu);
 460                fpmu = NULL;
 461                return EXIT_FAILURE;
 462        }
 463
 464        /* Increase header size by PMUFW file size */
 465        tparams->header_size += fsize(fpmu);
 466
 467        /* Allocate buffer with space for PMUFW */
 468        dynamic_header = calloc(1, tparams->header_size);
 469        tparams->hdr = dynamic_header;
 470
 471        return EXIT_SUCCESS;
 472}
 473
 474U_BOOT_IMAGE_TYPE(
 475        zynqmpimage,
 476        "Xilinx ZynqMP Boot Image support",
 477        sizeof(struct zynqmp_header),
 478        (void *)&zynqmpimage_header,
 479        zynqmpimage_check_params,
 480        zynqmpimage_verify_header,
 481        zynqmpimage_print_header,
 482        zynqmpimage_set_header,
 483        NULL,
 484        zynqmpimage_check_image_types,
 485        NULL,
 486        zynqmpimage_vrec_header
 487);
 488