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