1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2012 Samsung Electronics 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 #include <errno.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <compiler.h> 14 15 #define CHECKSUM_OFFSET (14*1024-4) 16 #define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP \ 17 | S_IWGRP | S_IROTH | S_IWOTH) 18 /* 19 * Requirement for the fixed size SPL header: 20 * IROM code reads first (CHECKSUM_OFFSET + 4) bytes from boot device. It then 21 * calculates the checksum of CHECKSUM_OFFSET bytes and compares with data at 22 * CHECKSUM_OFFSET location. 23 * 24 * Requirement for the variable size SPL header: 25 26 * IROM code reads the below header to find out the size of the blob (total 27 * size, header size included) and its checksum. Then it reads the rest of the 28 * blob [i.e size - sizeof(struct var_size_header) bytes], calculates the 29 * checksum and compares it with value read from the header. 30 */ 31 struct var_size_header { 32 uint32_t spl_size; 33 uint32_t spl_checksum; 34 uint32_t reserved[2]; 35 }; 36 37 static const char *prog_name; 38 39 static void write_to_file(int ofd, void *buffer, int size) 40 { 41 if (write(ofd, buffer, size) == size) 42 return; 43 44 fprintf(stderr, "%s: Failed to write to output file: %s\n", 45 prog_name, strerror(errno)); 46 exit(EXIT_FAILURE); 47 } 48 49 /* 50 * The argv is expected to include one optional parameter and two filenames: 51 * [--vs] IN OUT 52 * 53 * --vs - turns on the variable size SPL mode 54 * IN - the u-boot SPL binary, usually u-boot-spl.bin 55 * OUT - the prepared SPL blob, usually ${BOARD}-spl.bin 56 * 57 * This utility first reads the "u-boot-spl.bin" into a buffer. In case of 58 * fixed size SPL the buffer size is exactly CHECKSUM_OFFSET (such that 59 * smaller u-boot-spl.bin gets padded with 0xff bytes, the larger than limit 60 * u-boot-spl.bin causes an error). For variable size SPL the buffer size is 61 * eqaul to size of the IN file. 62 * 63 * Then it calculates checksum of the buffer by just summing up all bytes. 64 * Then 65 * 66 * - for fixed size SPL the buffer is written into the output file and the 67 * checksum is appended to the file in little endian format, which results 68 * in checksum added exactly at CHECKSUM_OFFSET. 69 * 70 * - for variable size SPL the checksum and file size are stored in the 71 * var_size_header structure (again, in little endian format) and the 72 * structure is written into the output file. Then the buffer is written 73 * into the output file. 74 */ 75 int main(int argc, char **argv) 76 { 77 unsigned char *buffer; 78 int i, ifd, ofd; 79 uint32_t checksum = 0; 80 off_t len; 81 int var_size_flag, read_size, count; 82 struct stat stat; 83 const int if_index = argc - 2; /* Input file name index in argv. */ 84 const int of_index = argc - 1; /* Output file name index in argv. */ 85 86 /* Strip path off the program name. */ 87 prog_name = strrchr(argv[0], '/'); 88 if (prog_name) 89 prog_name++; 90 else 91 prog_name = argv[0]; 92 93 if ((argc < 3) || 94 (argc > 4) || 95 ((argc == 4) && strcmp(argv[1], "--vs"))) { 96 fprintf(stderr, "Usage: %s [--vs] <infile> <outfile>\n", 97 prog_name); 98 exit(EXIT_FAILURE); 99 } 100 101 /* four args mean variable size SPL wrapper is required */ 102 var_size_flag = (argc == 4); 103 104 ifd = open(argv[if_index], O_RDONLY); 105 if (ifd < 0) { 106 fprintf(stderr, "%s: Can't open %s: %s\n", 107 prog_name, argv[if_index], strerror(errno)); 108 exit(EXIT_FAILURE); 109 } 110 111 ofd = open(argv[of_index], O_WRONLY | O_CREAT | O_TRUNC, FILE_PERM); 112 if (ofd < 0) { 113 fprintf(stderr, "%s: Can't open %s: %s\n", 114 prog_name, argv[of_index], strerror(errno)); 115 exit(EXIT_FAILURE); 116 } 117 118 if (fstat(ifd, &stat)) { 119 fprintf(stderr, "%s: Unable to get size of %s: %s\n", 120 prog_name, argv[if_index], strerror(errno)); 121 exit(EXIT_FAILURE); 122 } 123 124 len = stat.st_size; 125 126 if (var_size_flag) { 127 read_size = len; 128 count = len; 129 } else { 130 if (len > CHECKSUM_OFFSET) { 131 fprintf(stderr, 132 "%s: %s is too big (exceeds %d bytes)\n", 133 prog_name, argv[if_index], CHECKSUM_OFFSET); 134 exit(EXIT_FAILURE); 135 } 136 count = CHECKSUM_OFFSET; 137 read_size = len; 138 } 139 140 buffer = malloc(count); 141 if (!buffer) { 142 fprintf(stderr, 143 "%s: Failed to allocate %d bytes to store %s\n", 144 prog_name, count, argv[if_index]); 145 exit(EXIT_FAILURE); 146 } 147 148 if (read(ifd, buffer, read_size) != read_size) { 149 fprintf(stderr, "%s: Can't read %s: %s\n", 150 prog_name, argv[if_index], strerror(errno)); 151 exit(EXIT_FAILURE); 152 } 153 154 /* Pad if needed with 0xff to make flashing faster. */ 155 if (read_size < count) 156 memset((char *)buffer + read_size, 0xff, count - read_size); 157 158 for (i = 0, checksum = 0; i < count; i++) 159 checksum += buffer[i]; 160 checksum = cpu_to_le32(checksum); 161 162 if (var_size_flag) { 163 /* Prepare and write out the variable size SPL header. */ 164 struct var_size_header vsh; 165 uint32_t spl_size; 166 167 memset(&vsh, 0, sizeof(vsh)); 168 memcpy(&vsh.spl_checksum, &checksum, sizeof(checksum)); 169 170 spl_size = cpu_to_le32(count + sizeof(struct var_size_header)); 171 memcpy(&vsh.spl_size, &spl_size, sizeof(spl_size)); 172 write_to_file(ofd, &vsh, sizeof(vsh)); 173 } 174 175 write_to_file(ofd, buffer, count); 176 177 /* For fixed size SPL checksum is appended in the end. */ 178 if (!var_size_flag) 179 write_to_file(ofd, &checksum, sizeof(checksum)); 180 181 close(ifd); 182 close(ofd); 183 free(buffer); 184 185 return EXIT_SUCCESS; 186 } 187