• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/nec-usbatermfw.c

  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