// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 Marvell International Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mkimage.h" #include "../arch/mips/mach-octeon/include/mach/cvmx-bootloader.h" #define BUF_SIZE (16 * 1024) #define NAME_LEN 100 /* word offset */ #define WOFFSETOF(type, elem) (offsetof(type, elem) / 4) static int stage2_flag; static int stage_1_5_flag; static int stage_1_flag; /* Getoptions variables must be global */ static int failsafe_flag; static int pciboot_flag; static int env_flag; static const struct option long_options[] = { /* These options set a flag. */ {"failsafe", no_argument, &failsafe_flag, 1}, {"pciboot", no_argument, &pciboot_flag, 1}, {"nandstage2", no_argument, &stage2_flag, 1}, {"spistage2", no_argument, &stage2_flag, 1}, {"norstage2", no_argument, &stage2_flag, 1}, {"stage2", no_argument, &stage2_flag, 1}, {"stage1.5", no_argument, &stage_1_5_flag, 1}, {"stage1", no_argument, &stage_1_flag, 1}, {"environment", no_argument, &env_flag, 1}, /* * These options don't set a flag. * We distinguish them by their indices. */ {"board", required_argument, 0, 0}, {"text_base", required_argument, 0, 0}, {0, 0, 0, 0} }; static int lookup_board_type(char *board_name) { int i; int board_type = 0; char *substr = NULL; /* Detect stage 2 bootloader boards */ if (strcasestr(board_name, "_stage2")) { printf("Stage 2 bootloader detected from substring %s in name %s\n", "_stage2", board_name); stage2_flag = 1; } else { printf("Stage 2 bootloader NOT detected from name \"%s\"\n", board_name); } if (strcasestr(board_name, "_stage1")) { printf("Stage 1 bootloader detected from substring %s in name %s\n", "_stage1", board_name); stage_1_flag = 1; } /* Generic is a special case since there are numerous sub-types */ if (!strncasecmp("generic", board_name, strlen("generic"))) return CVMX_BOARD_TYPE_GENERIC; /* * If we're an eMMC stage 2 bootloader, cut off the _emmc_stage2 * part of the name. */ substr = strcasestr(board_name, "_emmc_stage2"); if (substr && (substr[strlen("_emmc_stage2")] == '\0')) { /*return CVMX_BOARD_TYPE_GENERIC;*/ printf(" Converting board name %s to ", board_name); *substr = '\0'; printf("%s\n", board_name); } /* * If we're a NAND stage 2 bootloader, cut off the _nand_stage2 * part of the name. */ substr = strcasestr(board_name, "_nand_stage2"); if (substr && (substr[strlen("_nand_stage2")] == '\0')) { /*return CVMX_BOARD_TYPE_GENERIC;*/ printf(" Converting board name %s to ", board_name); *substr = '\0'; printf("%s\n", board_name); } /* * If we're a SPI stage 2 bootloader, cut off the _spi_stage2 * part of the name. */ substr = strcasestr(board_name, "_spi_stage2"); if (substr && (substr[strlen("_spi_stage2")] == '\0')) { printf(" Converting board name %s to ", board_name); *substr = '\0'; printf("%s\n", board_name); } for (i = CVMX_BOARD_TYPE_NULL; i < CVMX_BOARD_TYPE_MAX; i++) if (!strcasecmp(cvmx_board_type_to_string(i), board_name)) board_type = i; for (i = CVMX_BOARD_TYPE_CUST_DEFINED_MIN; i < CVMX_BOARD_TYPE_CUST_DEFINED_MAX; i++) if (!strncasecmp(cvmx_board_type_to_string(i), board_name, strlen(cvmx_board_type_to_string(i)))) board_type = i; for (i = CVMX_BOARD_TYPE_CUST_PRIVATE_MIN; i < CVMX_BOARD_TYPE_CUST_PRIVATE_MAX; i++) if (!strncasecmp(cvmx_board_type_to_string(i), board_name, strlen(cvmx_board_type_to_string(i)))) board_type = i; return board_type; } static void usage(void) { printf("Usage: update_octeon_header [--failsafe] [--text_base=0xXXXXX]\n"); } int main(int argc, char *argv[]) { int fd; uint8_t buf[BUF_SIZE]; uint32_t data_crc = 0; int len; int data_len = 0; struct bootloader_header header; char filename[NAME_LEN]; int i; int option_index = 0; /* getopt_long stores the option index here. */ char board_name[NAME_LEN] = { 0 }; char tmp_board_name[NAME_LEN] = { 0 }; int c; int board_type = 0; unsigned long long address = 0; ssize_t ret; const char *type_str = NULL; int hdr_size = sizeof(struct bootloader_header); /* * Compile time check, if the size of the bootloader_header structure * has changed. */ compiletime_assert(sizeof(struct bootloader_header) == 192, "Octeon bootloader header size changed (!= 192)!"); /* Bail out, if argument count is incorrect */ if (argc < 3) { usage(); return -1; } debug("header size is: %d bytes\n", hdr_size); /* Parse command line options using getopt_long */ while (1) { c = getopt_long(argc, argv, "h", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { /* All long options handled in case 0 */ case 0: /* If this option set a flag, do nothing else now. */ if (long_options[option_index].flag != 0) break; debug("option(l) %s", long_options[option_index].name); if (!optarg) { usage(); return -1; } debug(" with arg %s\n", optarg); if (!strcmp(long_options[option_index].name, "board")) { if (strlen(optarg) >= NAME_LEN) { printf("strncpy() issue detected!"); exit(-1); } strncpy(board_name, optarg, NAME_LEN); printf("Using user supplied board name: %s\n", board_name); } else if (!strcmp(long_options[option_index].name, "text_base")) { address = strtoull(optarg, NULL, 0); printf("Address of image is: 0x%llx\n", (unsigned long long)address); if (!(address & 0xFFFFFFFFULL << 32)) { if (address & 1 << 31) { address |= 0xFFFFFFFFULL << 32; printf("Converting address to 64 bit compatibility space: 0x%llx\n", address); } } } break; case 'h': case '?': /* getopt_long already printed an error message. */ usage(); return -1; default: abort(); } } if (optind < argc) { /* * We only support one argument - an optional bootloader * file name */ if (argc - optind > 2) { fprintf(stderr, "non-option ARGV-elements: "); while (optind < argc) fprintf(stderr, "%s ", argv[optind++]); fprintf(stderr, "\n"); usage(); return -1; } } if (strlen(argv[optind]) >= NAME_LEN) { fprintf(stderr, "strncpy() issue detected!"); exit(-1); } strncpy(filename, argv[optind], NAME_LEN); if (board_name[0] == '\0') { if (strlen(argv[optind + 1]) >= NAME_LEN) { fprintf(stderr, "strncpy() issue detected!"); exit(-1); } strncpy(board_name, argv[optind + 1], NAME_LEN); } if (strlen(board_name) >= NAME_LEN) { fprintf(stderr, "strncpy() issue detected!"); exit(-1); } strncpy(tmp_board_name, board_name, NAME_LEN); fd = open(filename, O_RDWR); if (fd < 0) { fprintf(stderr, "Unable to open file: %s\n", filename); exit(-1); } if (failsafe_flag) printf("Setting failsafe flag\n"); if (strlen(board_name)) { int offset = 0; printf("Supplied board name of: %s\n", board_name); if (strstr(board_name, "failsafe")) { failsafe_flag = 1; printf("Setting failsafe flag based on board name\n"); } /* Skip leading octeon_ if present. */ if (!strncmp(board_name, "octeon_", 7)) offset = 7; /* * Check to see if 'failsafe' is in the name. If so, set the * failsafe flag. Also, ignore extra trailing characters on * passed parameter when comparing against board names. * We actually use the configuration name from u-boot, so it * may have some other variant names. Variants other than * failsafe _must_ be passed to this program explicitly */ board_type = lookup_board_type(board_name + offset); if (!board_type) { /* Retry with 'cust_' prefix to catch boards that are * in the customer section (such as nb5) */ sprintf(tmp_board_name, "cust_%s", board_name + offset); board_type = lookup_board_type(tmp_board_name); } /* reset to original value */ strncpy(tmp_board_name, board_name, NAME_LEN); if (!board_type) { /* * Retry with 'cust_private_' prefix to catch boards * that are in the customer private section */ sprintf(tmp_board_name, "cust_private_%s", board_name + offset); board_type = lookup_board_type(tmp_board_name); } if (!board_type) { fprintf(stderr, "ERROR: unable to determine board type\n"); exit(-1); } printf("Board type is: %d: %s\n", board_type, cvmx_board_type_to_string(board_type)); } else { fprintf(stderr, "Board name must be specified!\n"); exit(-1); } /* * Check to see if there is either an existing header, or that there * are zero valued bytes where we want to put the header */ len = read(fd, buf, BUF_SIZE); if (len > 0) { /* * Copy the header, as the first word (jump instruction, needs * to remain the same. */ memcpy(&header, buf, hdr_size); /* * Check to see if we have zero bytes (excluding first 4, which * are the jump instruction) */ for (i = 1; i < hdr_size / 4; i++) { if (((uint32_t *)buf)[i]) { fprintf(stderr, "ERROR: non-zero word found %x in location %d required for header, aborting\n", ((uint32_t *)buf)[i], i); exit(-1); } } printf("Zero bytes found in header location, adding header.\n"); } else { fprintf(stderr, "Unable to read from file %s\n", filename); exit(-1); } /* Read data bytes and generate CRC */ lseek(fd, hdr_size, SEEK_SET); while ((len = read(fd, buf, BUF_SIZE)) > 0) { data_crc = crc32(data_crc, buf, len); data_len += len; } printf("CRC of data: 0x%x, length: %d\n", data_crc, data_len); /* Now create the new header */ header.magic = htonl(BOOTLOADER_HEADER_MAGIC); header.maj_rev = htons(BOOTLOADER_HEADER_CURRENT_MAJOR_REV); header.min_rev = htons(BOOTLOADER_HEADER_CURRENT_MINOR_REV); header.dlen = htonl(data_len); header.dcrc = htonl(data_crc); header.board_type = htons(board_type); header.address = address; if (failsafe_flag) header.flags |= htonl(BL_HEADER_FLAG_FAILSAFE); printf("Stage 2 flag is %sset\n", stage2_flag ? "" : "not "); printf("Stage 1 flag is %sset\n", stage_1_flag ? "" : "not "); if (pciboot_flag) header.image_type = htons(BL_HEADER_IMAGE_PCIBOOT); else if (stage2_flag) header.image_type = htons(BL_HEADER_IMAGE_STAGE2); else if (stage_1_flag) header.image_type = htons(BL_HEADER_IMAGE_STAGE1); else if (env_flag) header.image_type = htons(BL_HEADER_IMAGE_UBOOT_ENV); else if (stage_1_5_flag || stage_1_flag) header.image_type = htons(BL_HEADER_IMAGE_PRE_UBOOT); else header.image_type = htons(BL_HEADER_IMAGE_NOR); switch (ntohs(header.image_type)) { case BL_HEADER_IMAGE_UNKNOWN: type_str = "Unknown"; break; case BL_HEADER_IMAGE_STAGE1: type_str = "Stage 1"; break; case BL_HEADER_IMAGE_STAGE2: type_str = "Stage 2"; break; case BL_HEADER_IMAGE_PRE_UBOOT: type_str = "Pre-U-Boot"; break; case BL_HEADER_IMAGE_STAGE3: type_str = "Stage 3"; break; case BL_HEADER_IMAGE_NOR: type_str = "NOR"; break; case BL_HEADER_IMAGE_PCIBOOT: type_str = "PCI Boot"; break; case BL_HEADER_IMAGE_UBOOT_ENV: type_str = "U-Boot Environment"; break; default: if (ntohs(header.image_type) >= BL_HEADER_IMAGE_CUST_RESERVED_MIN && ntohs(header.image_type) <= BL_HEADER_IMAGE_CUST_RESERVED_MAX) type_str = "Customer Reserved"; else type_str = "Unsupported"; } printf("Header image type: %s\n", type_str); header.hlen = htons(hdr_size); /* Now compute header CRC over all of the header excluding the CRC */ header.hcrc = crc32(0, (void *)&header, 12); header.hcrc = htonl(crc32(header.hcrc, ((void *)&(header)) + 16, hdr_size - 16)); /* Seek to beginning of file */ lseek(fd, 0, SEEK_SET); /* Write header to file */ ret = write(fd, &header, hdr_size); if (ret < 0) perror("write"); close(fd); printf("Header CRC: 0x%x\n", ntohl(header.hcrc)); return 0; }