1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2024 INAGAKI Hiroshi <musashino.open@gmail.com> 4 */ 5 6 #include <byteswap.h> 7 #include <endian.h> 8 #include <errno.h> 9 #include <libgen.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #define FWHDR_LEN 0x20 17 #define BLKHDR_LEN 0x18 18 19 #define BLKHDR_FLAGS_GZIP 0x80000000 20 #define BLKHDR_FLAGS_EXEC 0x00000002 21 #define BLKHDR_FLAGS_SYS 0x00000001 22 #define BLKHDR_DEF_LOADADDR 0x80040000 23 #define BLKHDR_DEF_ENTRYP BLKHDR_DEF_LOADADDR 24 25 #define FILETYPE_LEN 0x20 26 27 #define PADBLK_LEN 0x4 28 #define DATABLK_CNT_MAX 2 29 30 struct header 31 { 32 uint32_t flags; 33 uint32_t totlen; 34 uint32_t hdrlen; 35 uint32_t cksum; 36 uint32_t loadaddr; 37 uint32_t entryp; 38 } __attribute__ ((packed)); 39 40 static char *progname; 41 FILE *outbin; 42 int nblk = 0; 43 44 static void usage(void) 45 { 46 printf("Usage: %s <output> [OPTIONS...]\n", progname); 47 printf("\n" 48 "Options:\n" 49 " -t <type> set firmware type to <type>\n" 50 " -f <flags> set data flags to <flags>\n" 51 " -a <loadaddr> set data load address to <loadaddr>\n" 52 " -e <entry> set data entry point to <entry>\n" 53 " -d <file> read input data from file <file> (max. %dx)\n", 54 DATABLK_CNT_MAX); 55 } 56 57 static int strtou32(char *arg, uint32_t *val) 58 { 59 char *endptr = NULL; 60 61 errno = 0; 62 *val = strtoul(arg, &endptr, 0); 63 return (errno || (endptr && *endptr)) ? -EINVAL : 0; 64 } 65 66 static int write_data(const char *buf, size_t length, uint32_t *cksum) 67 { 68 size_t padlen = 0; 69 size_t pos; 70 71 if (length % PADBLK_LEN) 72 padlen = PADBLK_LEN - length % PADBLK_LEN; 73 74 if (fwrite(buf, 1, length, outbin) != length) { 75 fprintf(stderr, "Couldn't write %zu bytes\n", length); 76 return -EACCES; 77 } 78 79 if (cksum) 80 for (pos = 0; pos < length; pos += 2) { 81 if (length - pos == 1) 82 *cksum += le16toh(buf[pos]); 83 else 84 *cksum += le16toh(*(uint16_t *)(buf + pos)); 85 } 86 87 if (padlen) { 88 int i; 89 for (i = 0; i < padlen; i++) 90 fputc(0x0, outbin); 91 } 92 93 return padlen; 94 } 95 96 static int write_blockheader(uint32_t flags, 97 uint32_t loadaddr, uint32_t entryp, 98 size_t datalen, int padlen, uint32_t cksum) 99 { 100 struct header hdr; 101 uint16_t *cur = (uint16_t *)&hdr; 102 char buf[0x20]; 103 104 flags <<= 16; 105 flags |= (~flags & 0xffff0000) >> 16; 106 107 hdr.flags = htobe32(flags); 108 hdr.totlen = htobe32(BLKHDR_LEN + datalen); 109 hdr.hdrlen = htobe32(BLKHDR_LEN); 110 hdr.cksum = 0; 111 hdr.loadaddr = htobe32(loadaddr); 112 hdr.entryp = htobe32(entryp); 113 114 for (cur += 2; cur - (uint16_t *)&hdr < BLKHDR_LEN / 2; cur++) { 115 cksum += htole16(*cur); 116 /* 117 * workaround of unknown bug if built with gcc 9.4.0 118 * (Ubuntu 9.4.0-1ubuntu1~20.04.2) and cmake 119 */ 120 snprintf(buf, sizeof(buf), "%u", cksum); 121 } 122 123 cksum = 0xffff ^ cksum % 0xffff; 124 hdr.cksum = htole32(cksum); 125 126 if (datalen) 127 fseek(outbin, -(BLKHDR_LEN + datalen + padlen), SEEK_CUR); 128 if (fwrite(&hdr, 1, BLKHDR_LEN, outbin) != BLKHDR_LEN) { 129 fprintf(stderr, "Couldn't write %d bytes\n", BLKHDR_LEN); 130 return -EACCES; 131 } 132 if (datalen) 133 fseek(outbin, datalen + padlen, SEEK_CUR); 134 135 nblk++; 136 printf("%d:\t0x%04x,\t0x%08zx,\t0x%08x,\t0x%08x,\t0x%08x\n", 137 nblk, flags >> 16, BLKHDR_LEN + datalen, loadaddr, entryp, cksum); 138 return 0; 139 } 140 141 static int append_datablock_from_file(uint32_t flags, 142 uint32_t loadaddr, uint32_t entryp, 143 const char *datapath) 144 { 145 FILE *databin; 146 size_t readlen, length = 0; 147 uint32_t cksum = 0; 148 char buf[0x10000]; 149 int ret = 0; 150 151 fseek(outbin, BLKHDR_LEN, SEEK_CUR); 152 153 databin = fopen(datapath, "r"); 154 if (!databin) { 155 fprintf(stderr, "couldn't open %s\n", datapath); 156 return -EACCES; 157 } 158 159 while ((readlen = fread(buf, 1, sizeof(buf), databin)) > 0) { 160 ret = write_data(buf, readlen, &cksum); 161 if (ret < 0) 162 goto exit; 163 length += readlen; 164 } 165 166 ret = write_blockheader(flags, loadaddr, entryp, length, ret, cksum); 167 168 exit: 169 fclose(databin); 170 return ret; 171 } 172 173 static int append_datablock_from_buf(uint32_t flags, 174 uint32_t loadaddr, uint32_t entryp, 175 const char *buf, size_t buflen) 176 { 177 uint32_t cksum = 0; 178 int ret; 179 180 fseek(outbin, BLKHDR_LEN, SEEK_CUR); 181 182 ret = write_data(buf, buflen, &cksum); 183 184 return write_blockheader(flags, loadaddr, entryp, buflen, ret, cksum); 185 } 186 187 int main(int argc, char **argv) 188 { 189 uint32_t flags = BLKHDR_FLAGS_EXEC; 190 uint32_t loadaddr = BLKHDR_DEF_LOADADDR; 191 uint32_t entryp = BLKHDR_DEF_ENTRYP; 192 char buf[0x40], ftype[FILETYPE_LEN]; 193 int ret, c, ftlen = 0; 194 195 progname = basename(argv[0]); 196 197 if (argc >= 2 && 198 (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { 199 usage(); 200 return 0; 201 } 202 203 if (argc < 2) { 204 fprintf(stderr, "no output file specified\n"); 205 usage(); 206 return -EINVAL; 207 } 208 209 outbin = fopen(argv[1], "w+"); 210 if (!outbin) { 211 fprintf(stderr, "Couldn't open %s\n", argv[1]); 212 return -EACCES; 213 } 214 215 /* add firmware header */ 216 sprintf(buf, "USB ATERMWL3050"); 217 memset(buf + 0x10, 0xff, 0x10); 218 ret = write_data(buf, FWHDR_LEN, NULL); 219 if (ret < 0) 220 goto exit; 221 222 printf("\tFlags\tTotal Len.\tLoad Addr\tEntry Point\tChecksum\n"); 223 224 /* add version/copyright block */ 225 ret = sprintf(buf, "VERSION: 9.99.99\nOEM1 VERSION: 9.9.99\n"); 226 ret = append_datablock_from_buf(0x0, 0x0, 0x0, buf, ret); 227 if (ret) 228 goto exit; 229 230 /* set type and parse/write user-defined data blocks */ 231 while((c = getopt(argc, argv, "f:a:e:d:t:")) != -1) { 232 switch (c) { 233 case 'f': 234 if (strtou32(optarg, &flags)) { 235 fprintf(stderr, "invalid flags value specified\n"); 236 ret = -EINVAL; 237 goto exit; 238 } 239 break; 240 case 'a': 241 if (strtou32(optarg, &loadaddr)) { 242 fprintf(stderr, "invalid load address specified\n"); 243 ret = -EINVAL; 244 goto exit; 245 } 246 break; 247 case 'e': 248 if (strtou32(optarg, &entryp)) { 249 fprintf(stderr, "invalid entry point specified\n"); 250 ret = -EINVAL; 251 goto exit; 252 } 253 break; 254 case 'd': 255 if (nblk - 1 >= DATABLK_CNT_MAX) { 256 fprintf(stderr, 257 "data block count exceeds maximum count (%d), skipping...\n", 258 DATABLK_CNT_MAX); 259 continue; 260 } 261 ret = append_datablock_from_file(flags, loadaddr, entryp, optarg); 262 if (ret) 263 goto exit; 264 265 flags = BLKHDR_FLAGS_EXEC; 266 loadaddr = BLKHDR_DEF_LOADADDR; 267 entryp = BLKHDR_DEF_ENTRYP; 268 break; 269 case 't': 270 ftlen = snprintf(buf, sizeof(buf), "Binary Type%s File END \r\n", optarg); 271 if (ftlen > FILETYPE_LEN) { 272 fprintf(stderr, "specified type is too long\n"); 273 ret = -EINVAL; 274 goto exit; 275 } 276 memset(ftype, 0xff, FILETYPE_LEN); 277 strncpy(ftype + (FILETYPE_LEN - ftlen), buf, ftlen); 278 break; 279 case '?': 280 default: 281 ret = -EINVAL; 282 goto exit; 283 } 284 } 285 286 if (!ftlen) { 287 fprintf(stderr, "no file type specified\n"); 288 ret = -EINVAL; 289 goto exit; 290 } 291 292 /* append end block */ 293 ret = write_blockheader(BLKHDR_FLAGS_SYS, 0, 0, 0, 0, 0); 294 if (ret) 295 goto exit; 296 297 /* append file type */ 298 ret = write_data(ftype, FILETYPE_LEN, NULL); 299 300 exit: 301 fclose(outbin); 302 if (ret == -EINVAL) 303 usage(); 304 return ret < 0 ? ret : 0; 305 } 306
This page was automatically generated by LXR 0.3.1. • OpenWrt