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