uboot/tools/update_octeon_header.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2020 Marvell International Ltd.
   4 */
   5
   6#include <stdio.h>
   7#include <stdint.h>
   8#include <stddef.h>
   9#include <sys/types.h>
  10#include <sys/stat.h>
  11#include <fcntl.h>
  12#include <unistd.h>
  13#include <stdbool.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <getopt.h>
  17#include <arpa/inet.h>
  18#include <linux/compiler.h>
  19#include <u-boot/crc.h>
  20
  21#include "mkimage.h"
  22
  23#include "../arch/mips/mach-octeon/include/mach/cvmx-bootloader.h"
  24
  25#define BUF_SIZE        (16 * 1024)
  26#define NAME_LEN        100
  27
  28/* word offset */
  29#define WOFFSETOF(type, elem)   (offsetof(type, elem) / 4)
  30
  31static int stage2_flag;
  32static int stage_1_5_flag;
  33static int stage_1_flag;
  34
  35/* Getoptions variables must be global */
  36static int failsafe_flag;
  37static int pciboot_flag;
  38static int env_flag;
  39
  40static const struct option long_options[] = {
  41        /* These options set a flag. */
  42        {"failsafe", no_argument, &failsafe_flag, 1},
  43        {"pciboot", no_argument, &pciboot_flag, 1},
  44        {"nandstage2", no_argument, &stage2_flag, 1},
  45        {"spistage2", no_argument, &stage2_flag, 1},
  46        {"norstage2", no_argument, &stage2_flag, 1},
  47        {"stage2", no_argument, &stage2_flag, 1},
  48        {"stage1.5", no_argument, &stage_1_5_flag, 1},
  49        {"stage1", no_argument, &stage_1_flag, 1},
  50        {"environment", no_argument, &env_flag, 1},
  51        /*
  52         * These options don't set a flag.
  53         * We distinguish them by their indices.
  54         */
  55        {"board", required_argument, 0, 0},
  56        {"text_base", required_argument, 0, 0},
  57        {0, 0, 0, 0}
  58};
  59
  60static int lookup_board_type(char *board_name)
  61{
  62        int i;
  63        int board_type = 0;
  64        char *substr = NULL;
  65
  66        /* Detect stage 2 bootloader boards */
  67        if (strcasestr(board_name, "_stage2")) {
  68                printf("Stage 2 bootloader detected from substring %s in name %s\n",
  69                       "_stage2", board_name);
  70                stage2_flag = 1;
  71        } else {
  72                printf("Stage 2 bootloader NOT detected from name \"%s\"\n",
  73                       board_name);
  74        }
  75
  76        if (strcasestr(board_name, "_stage1")) {
  77                printf("Stage 1 bootloader detected from substring %s in name %s\n",
  78                       "_stage1", board_name);
  79                stage_1_flag = 1;
  80        }
  81
  82        /* Generic is a special case since there are numerous sub-types */
  83        if (!strncasecmp("generic", board_name, strlen("generic")))
  84                return CVMX_BOARD_TYPE_GENERIC;
  85
  86        /*
  87         * If we're an eMMC stage 2 bootloader, cut off the _emmc_stage2
  88         * part of the name.
  89         */
  90        substr = strcasestr(board_name, "_emmc_stage2");
  91        if (substr && (substr[strlen("_emmc_stage2")] == '\0')) {
  92                /*return CVMX_BOARD_TYPE_GENERIC;*/
  93
  94                printf("  Converting board name %s to ", board_name);
  95                *substr = '\0';
  96                printf("%s\n", board_name);
  97        }
  98
  99        /*
 100         * If we're a NAND stage 2 bootloader, cut off the _nand_stage2
 101         * part of the name.
 102         */
 103        substr = strcasestr(board_name, "_nand_stage2");
 104        if (substr && (substr[strlen("_nand_stage2")] == '\0')) {
 105                /*return CVMX_BOARD_TYPE_GENERIC;*/
 106
 107                printf("  Converting board name %s to ", board_name);
 108                *substr = '\0';
 109                printf("%s\n", board_name);
 110        }
 111
 112        /*
 113         * If we're a SPI stage 2 bootloader, cut off the _spi_stage2
 114         * part of the name.
 115         */
 116        substr = strcasestr(board_name, "_spi_stage2");
 117        if (substr && (substr[strlen("_spi_stage2")] == '\0')) {
 118                printf("  Converting board name %s to ", board_name);
 119                *substr = '\0';
 120                printf("%s\n", board_name);
 121        }
 122
 123        for (i = CVMX_BOARD_TYPE_NULL; i < CVMX_BOARD_TYPE_MAX; i++)
 124                if (!strcasecmp(cvmx_board_type_to_string(i), board_name))
 125                        board_type = i;
 126
 127        for (i = CVMX_BOARD_TYPE_CUST_DEFINED_MIN;
 128             i < CVMX_BOARD_TYPE_CUST_DEFINED_MAX; i++)
 129                if (!strncasecmp(cvmx_board_type_to_string(i), board_name,
 130                                 strlen(cvmx_board_type_to_string(i))))
 131                        board_type = i;
 132
 133        for (i = CVMX_BOARD_TYPE_CUST_PRIVATE_MIN;
 134             i < CVMX_BOARD_TYPE_CUST_PRIVATE_MAX; i++)
 135                if (!strncasecmp(cvmx_board_type_to_string(i), board_name,
 136                                 strlen(cvmx_board_type_to_string(i))))
 137                        board_type = i;
 138
 139        return board_type;
 140}
 141
 142static void usage(void)
 143{
 144        printf("Usage: update_octeon_header <filename> <board_name> [--failsafe] [--text_base=0xXXXXX]\n");
 145}
 146
 147int main(int argc, char *argv[])
 148{
 149        int fd;
 150        uint8_t buf[BUF_SIZE];
 151        uint32_t data_crc = 0;
 152        int len;
 153        int data_len = 0;
 154        struct bootloader_header header;
 155        char filename[NAME_LEN];
 156        int i;
 157        int option_index = 0;   /* getopt_long stores the option index here. */
 158        char board_name[NAME_LEN] = { 0 };
 159        char tmp_board_name[NAME_LEN] = { 0 };
 160        int c;
 161        int board_type = 0;
 162        unsigned long long address = 0;
 163        ssize_t ret;
 164        const char *type_str = NULL;
 165        int hdr_size = sizeof(struct bootloader_header);
 166
 167        /*
 168         * Compile time check, if the size of the bootloader_header structure
 169         * has changed.
 170         */
 171        compiletime_assert(sizeof(struct bootloader_header) == 192,
 172                           "Octeon bootloader header size changed (!= 192)!");
 173
 174        /* Bail out, if argument count is incorrect */
 175        if (argc < 3) {
 176                usage();
 177                return -1;
 178        }
 179
 180        debug("header size is: %d bytes\n", hdr_size);
 181
 182        /* Parse command line options using getopt_long */
 183        while (1) {
 184                c = getopt_long(argc, argv, "h", long_options, &option_index);
 185
 186                /* Detect the end of the options. */
 187                if (c == -1)
 188                        break;
 189
 190                switch (c) {
 191                        /* All long options handled in case 0 */
 192                case 0:
 193                        /* If this option set a flag, do nothing else now. */
 194                        if (long_options[option_index].flag != 0)
 195                                break;
 196                        debug("option(l) %s", long_options[option_index].name);
 197
 198                        if (!optarg) {
 199                                usage();
 200                                return -1;
 201                        }
 202                        debug(" with arg %s\n", optarg);
 203
 204                        if (!strcmp(long_options[option_index].name, "board")) {
 205                                if (strlen(optarg) >= NAME_LEN) {
 206                                        printf("strncpy() issue detected!");
 207                                        exit(-1);
 208                                }
 209                                strncpy(board_name, optarg, NAME_LEN);
 210
 211                                printf("Using user supplied board name: %s\n",
 212                                       board_name);
 213                        } else if (!strcmp(long_options[option_index].name,
 214                                           "text_base")) {
 215                                address = strtoull(optarg, NULL, 0);
 216                                printf("Address of image is: 0x%llx\n",
 217                                       (unsigned long long)address);
 218                                if (!(address & 0xFFFFFFFFULL << 32)) {
 219                                        if (address & 1 << 31) {
 220                                                address |= 0xFFFFFFFFULL << 32;
 221                                                printf("Converting address to 64 bit compatibility space: 0x%llx\n",
 222                                                       address);
 223                                        }
 224                                }
 225                        }
 226                        break;
 227
 228                case 'h':
 229                case '?':
 230                        /* getopt_long already printed an error message. */
 231                        usage();
 232                        return -1;
 233
 234                default:
 235                        abort();
 236                }
 237        }
 238
 239        if (optind < argc) {
 240                /*
 241                 * We only support one argument - an optional bootloader
 242                 * file name
 243                 */
 244                if (argc - optind > 2) {
 245                        fprintf(stderr, "non-option ARGV-elements: ");
 246                        while (optind < argc)
 247                                fprintf(stderr, "%s ", argv[optind++]);
 248                        fprintf(stderr, "\n");
 249
 250                        usage();
 251                        return -1;
 252                }
 253        }
 254
 255        if (strlen(argv[optind]) >= NAME_LEN) {
 256                fprintf(stderr, "strncpy() issue detected!");
 257                exit(-1);
 258        }
 259        strncpy(filename, argv[optind], NAME_LEN);
 260
 261        if (board_name[0] == '\0') {
 262                if (strlen(argv[optind + 1]) >= NAME_LEN) {
 263                        fprintf(stderr, "strncpy() issue detected!");
 264                        exit(-1);
 265                }
 266                strncpy(board_name, argv[optind + 1], NAME_LEN);
 267        }
 268
 269        if (strlen(board_name) >= NAME_LEN) {
 270                fprintf(stderr, "strncpy() issue detected!");
 271                exit(-1);
 272        }
 273        strncpy(tmp_board_name, board_name, NAME_LEN);
 274
 275        fd = open(filename, O_RDWR);
 276        if (fd < 0) {
 277                fprintf(stderr, "Unable to open file: %s\n", filename);
 278                exit(-1);
 279        }
 280
 281        if (failsafe_flag)
 282                printf("Setting failsafe flag\n");
 283
 284        if (strlen(board_name)) {
 285                int offset = 0;
 286
 287                printf("Supplied board name of: %s\n", board_name);
 288
 289                if (strstr(board_name, "failsafe")) {
 290                        failsafe_flag = 1;
 291                        printf("Setting failsafe flag based on board name\n");
 292                }
 293                /* Skip leading octeon_ if present. */
 294                if (!strncmp(board_name, "octeon_", 7))
 295                        offset = 7;
 296
 297                /*
 298                 * Check to see if 'failsafe' is in the name.  If so, set the
 299                 * failsafe flag.  Also, ignore extra trailing characters on
 300                 * passed parameter when comparing against board names.
 301                 * We actually use the configuration name from u-boot, so it
 302                 * may have some other variant names.  Variants other than
 303                 * failsafe _must_ be passed to this program explicitly
 304                 */
 305
 306                board_type = lookup_board_type(board_name + offset);
 307                if (!board_type) {
 308                        /* Retry with 'cust_' prefix to catch boards that are
 309                         * in the customer section (such as nb5)
 310                         */
 311                        sprintf(tmp_board_name, "cust_%s", board_name + offset);
 312                        board_type = lookup_board_type(tmp_board_name);
 313                }
 314
 315                /* reset to original value */
 316                strncpy(tmp_board_name, board_name, NAME_LEN);
 317                if (!board_type) {
 318                        /*
 319                         * Retry with 'cust_private_' prefix to catch boards
 320                         * that are in the customer private section
 321                         */
 322                        sprintf(tmp_board_name, "cust_private_%s",
 323                                board_name + offset);
 324                        board_type = lookup_board_type(tmp_board_name);
 325                }
 326
 327                if (!board_type) {
 328                        fprintf(stderr,
 329                                "ERROR: unable to determine board type\n");
 330                        exit(-1);
 331                }
 332                printf("Board type is: %d: %s\n", board_type,
 333                       cvmx_board_type_to_string(board_type));
 334        } else {
 335                fprintf(stderr, "Board name must be specified!\n");
 336                exit(-1);
 337        }
 338
 339        /*
 340         * Check to see if there is either an existing header, or that there
 341         * are zero valued bytes where we want to put the header
 342         */
 343        len = read(fd, buf, BUF_SIZE);
 344        if (len > 0) {
 345                /*
 346                 * Copy the header, as the first word (jump instruction, needs
 347                 * to remain the same.
 348                 */
 349                memcpy(&header, buf, hdr_size);
 350                /*
 351                 * Check to see if we have zero bytes (excluding first 4, which
 352                 * are the jump instruction)
 353                 */
 354                for (i = 1; i < hdr_size / 4; i++) {
 355                        if (((uint32_t *)buf)[i]) {
 356                                fprintf(stderr,
 357                                        "ERROR: non-zero word found %x in location %d required for header, aborting\n",
 358                                       ((uint32_t *)buf)[i], i);
 359                                exit(-1);
 360                        }
 361                }
 362                printf("Zero bytes found in header location, adding header.\n");
 363
 364        } else {
 365                fprintf(stderr, "Unable to read from file %s\n", filename);
 366                exit(-1);
 367        }
 368
 369        /* Read data bytes and generate CRC */
 370        lseek(fd, hdr_size, SEEK_SET);
 371
 372        while ((len = read(fd, buf, BUF_SIZE)) > 0) {
 373                data_crc = crc32(data_crc, buf, len);
 374                data_len += len;
 375        }
 376        printf("CRC of data: 0x%x, length: %d\n", data_crc, data_len);
 377
 378        /* Now create the new header */
 379        header.magic = htonl(BOOTLOADER_HEADER_MAGIC);
 380        header.maj_rev = htons(BOOTLOADER_HEADER_CURRENT_MAJOR_REV);
 381        header.min_rev = htons(BOOTLOADER_HEADER_CURRENT_MINOR_REV);
 382        header.dlen = htonl(data_len);
 383        header.dcrc = htonl(data_crc);
 384        header.board_type = htons(board_type);
 385        header.address = address;
 386        if (failsafe_flag)
 387                header.flags |= htonl(BL_HEADER_FLAG_FAILSAFE);
 388
 389        printf("Stage 2 flag is %sset\n", stage2_flag ? "" : "not ");
 390        printf("Stage 1 flag is %sset\n", stage_1_flag ? "" : "not ");
 391        if (pciboot_flag)
 392                header.image_type = htons(BL_HEADER_IMAGE_PCIBOOT);
 393        else if (stage2_flag)
 394                header.image_type = htons(BL_HEADER_IMAGE_STAGE2);
 395        else if (stage_1_flag)
 396                header.image_type = htons(BL_HEADER_IMAGE_STAGE1);
 397        else if (env_flag)
 398                header.image_type = htons(BL_HEADER_IMAGE_UBOOT_ENV);
 399        else if (stage_1_5_flag || stage_1_flag)
 400                header.image_type = htons(BL_HEADER_IMAGE_PRE_UBOOT);
 401        else
 402                header.image_type = htons(BL_HEADER_IMAGE_NOR);
 403
 404        switch (ntohs(header.image_type)) {
 405        case BL_HEADER_IMAGE_UNKNOWN:
 406                type_str = "Unknown";
 407                break;
 408        case BL_HEADER_IMAGE_STAGE1:
 409                type_str = "Stage 1";
 410                break;
 411        case BL_HEADER_IMAGE_STAGE2:
 412                type_str = "Stage 2";
 413                break;
 414        case BL_HEADER_IMAGE_PRE_UBOOT:
 415                type_str = "Pre-U-Boot";
 416                break;
 417        case BL_HEADER_IMAGE_STAGE3:
 418                type_str = "Stage 3";
 419                break;
 420        case BL_HEADER_IMAGE_NOR:
 421                type_str = "NOR";
 422                break;
 423        case BL_HEADER_IMAGE_PCIBOOT:
 424                type_str = "PCI Boot";
 425                break;
 426        case BL_HEADER_IMAGE_UBOOT_ENV:
 427                type_str = "U-Boot Environment";
 428                break;
 429        default:
 430                if (ntohs(header.image_type) >= BL_HEADER_IMAGE_CUST_RESERVED_MIN &&
 431                    ntohs(header.image_type) <= BL_HEADER_IMAGE_CUST_RESERVED_MAX)
 432                        type_str = "Customer Reserved";
 433                else
 434                        type_str = "Unsupported";
 435        }
 436        printf("Header image type: %s\n", type_str);
 437        header.hlen = htons(hdr_size);
 438
 439        /* Now compute header CRC over all of the header excluding the CRC */
 440        header.hcrc = crc32(0, (void *)&header, 12);
 441        header.hcrc = htonl(crc32(header.hcrc, ((void *)&(header)) + 16,
 442                                  hdr_size - 16));
 443
 444        /* Seek to beginning of file */
 445        lseek(fd, 0, SEEK_SET);
 446
 447        /* Write header to file */
 448        ret = write(fd, &header, hdr_size);
 449        if (ret < 0)
 450                perror("write");
 451
 452        close(fd);
 453
 454        printf("Header CRC: 0x%x\n", ntohl(header.hcrc));
 455        return 0;
 456}
 457