tools: mtk_image: split gfh header verification into a new function
[platform/kernel/u-boot.git] / tools / update_octeon_header.c
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
31 static int stage2_flag;
32 static int stage_1_5_flag;
33 static int stage_1_flag;
34
35 /* Getoptions variables must be global */
36 static int failsafe_flag;
37 static int pciboot_flag;
38 static int env_flag;
39
40 static 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
60 static 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
142 static void usage(void)
143 {
144         printf("Usage: update_octeon_header <filename> <board_name> [--failsafe] [--text_base=0xXXXXX]\n");
145 }
146
147 int 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 }