1/* 2 * Make CHK Image 3 * 4 * This utility creates Netgear .chk files. 5 * 6 * Copyright (C) 2008 Dave C. Reeve <Dave.Reeve@dreeve.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 */ 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <stdarg.h> 26#include <errno.h> 27#include <arpa/inet.h> 28#include <unistd.h> 29 30#define BUF_LEN (2048) 31 32#define MAX_BOARD_ID_LEN (64) 33 34struct chk_header { 35 uint32_t magic; 36 uint32_t header_len; 37 uint8_t reserved[8]; 38 uint32_t kernel_chksum; 39 uint32_t rootfs_chksum; 40 uint32_t kernel_len; 41 uint32_t rootfs_len; 42 uint32_t image_chksum; 43 uint32_t header_chksum; 44 /* char board_id[] - upto MAX_BOARD_ID_LEN */ 45}; 46 47static void __attribute__ ((format (printf, 2, 3))) 48fatal_error (int maybe_errno, const char * format, ...) 49{ 50 va_list ap; 51 52 fprintf (stderr, "mkchkimg: "); 53 va_start (ap, format); 54 vfprintf (stderr, format, ap); 55 va_end (ap); 56 57 if (maybe_errno) { 58 fprintf (stderr, ": %s\n", strerror (maybe_errno)); 59 } else { 60 fprintf (stderr, "\n"); 61 } 62 63 exit (EXIT_FAILURE); 64} 65 66static void __attribute__ ((format (printf, 1, 2))) 67message (const char * format, ...) 68{ 69 va_list ap; 70 71 fprintf (stderr, "mkchkimg: "); 72 va_start (ap, format); 73 vfprintf (stderr, format, ap); 74 va_end (ap); 75 fprintf (stderr, "\n"); 76} 77 78struct ngr_checksum { 79 uint32_t c0; 80 uint32_t c1; 81}; 82 83static inline void 84netgear_checksum_init (struct ngr_checksum * c) 85{ 86 c->c0 = c->c1 = 0; 87} 88 89static inline void 90netgear_checksum_add (struct ngr_checksum * c, unsigned char * buf, size_t len) 91{ 92 size_t i; 93 94 for (i=0; i<len; i++) { 95 c->c0 += buf[i] & 0xff; 96 c->c1 += c->c0; 97 } 98} 99 100static inline unsigned long 101netgear_checksum_fini (struct ngr_checksum * c) 102{ 103 uint32_t b, checksum; 104 105 b = (c->c0 & 65535) + ((c->c0 >> 16) & 65535); 106 c->c0 = ((b >> 16) + b) & 65535; 107 b = (c->c1 & 65535) + ((c->c1 >> 16) & 65535); 108 c->c1 = ((b >> 16) + b) & 65535; 109 checksum = ((c->c1 << 16) | c->c0); 110 return checksum; 111} 112 113static void 114print_help (void) 115{ 116 fprintf (stderr, "Usage: mkchkimg -o output -k kernel [-f filesys] [-b board_id] [-r region]\n"); 117} 118 119int 120main (int argc, char * argv[]) 121{ 122 int opt; 123 char * ptr; 124 size_t len; 125 size_t header_len; 126 struct chk_header * hdr; 127 struct ngr_checksum chk_part, chk_whole; 128 char buf[BUF_LEN]; 129 char * output_file, * kern_file, * fs_file; 130 FILE * out_fp, * kern_fp, * fs_fp; 131 char * board_id; 132 unsigned long region; 133 134 /* Default values */ 135 board_id = "U12H072T00_NETGEAR"; 136 region = 1; /* 1=WW, 2=NA */ 137 output_file = NULL; 138 kern_file = NULL; 139 fs_file = NULL; 140 fs_fp = NULL; 141 142 while ((opt = getopt (argc, argv, ":b:r:k:f:o:h")) != -1) { 143 switch (opt) { 144 case 'b': 145 /* Board Identity */ 146 if (strlen (optarg) > MAX_BOARD_ID_LEN) { 147 fatal_error (0, "Board lenght exceeds %d", 148 MAX_BOARD_ID_LEN); 149 } 150 board_id = optarg; 151 break; 152 153 case 'r': 154 /* Region */ 155 errno = 0; 156 region = strtoul (optarg, &ptr, 0); 157 if (errno || ptr==optarg || *ptr!='\0') { 158 fatal_error (0, "Cannot parse region %s", optarg); 159 } 160 if (region > 0xff) { 161 fatal_error (0, "Region cannot exceed 0xff"); 162 } 163 break; 164 165 case 'k': 166 /* Kernel */ 167 kern_file = optarg; 168 break; 169 170 case 'f': 171 /* Filing System */ 172 fs_file = optarg; 173 break; 174 175 case 'o': 176 /* Output file */ 177 output_file = optarg; 178 break; 179 180 case 'h': 181 print_help (); 182 return EXIT_SUCCESS; 183 184 case ':': 185 print_help (); 186 fatal_error (0, "Option -%c missing argument", optopt); 187 break; 188 189 case '?': 190 print_help (); 191 fatal_error (0, "Unknown argument -%c", optopt); 192 break; 193 194 default: 195 break; 196 } 197 } 198 199 /* Check we have all the options expected */ 200 if (!kern_file) { 201 print_help (); 202 fatal_error (0, "Kernel file expected"); 203 } 204 if (!output_file) { 205 print_help (); 206 fatal_error (0, "Output file required"); 207 } 208 message ("Netgear CHK writer - v0.1"); 209 210 /* Open the input file */ 211 kern_fp = fopen (kern_file, "r"); 212 if (!kern_fp) { 213 fatal_error (errno, "Cannot open %s", kern_file); 214 } 215 216 /* Open the fs file, if specified */ 217 if (fs_file) { 218 fs_fp = fopen (fs_file, "r"); 219 if (!fs_fp) { 220 fatal_error (errno, "Cannot open %s", fs_file); 221 } 222 } 223 224 /* Open the output file */ 225 out_fp = fopen (output_file, "w+"); 226 if (!out_fp) { 227 fatal_error (errno, "Cannot open %s", output_file); 228 } 229 230 /* Write zeros when the chk header will be */ 231 buf[0] = '\0'; 232 header_len = sizeof (struct chk_header) + strlen (board_id); 233 if (fwrite (buf, 1, header_len, out_fp) != header_len) { 234 fatal_error (errno, "Cannot write header"); 235 } 236 237 /* Allocate storage for header, we fill in as we go */ 238 hdr = malloc (sizeof (struct chk_header)); 239 if (!hdr) { 240 fatal_error (0, "malloc failed"); 241 } 242 bzero (hdr, sizeof (struct chk_header)); 243 244 /* Fill in known values */ 245 hdr->magic = htonl (0x2a23245e); 246 hdr->header_len = htonl(header_len); 247 hdr->reserved[0] = (unsigned char)(region & 0xff); 248 hdr->reserved[1] = 1; /* Major */ 249 hdr->reserved[2] = 1; /* Minor */ 250 hdr->reserved[3] = 99; /* Build */ 251 hdr->reserved[4] = 0; /* Unknown t1 ? was 1 */ 252 hdr->reserved[5] = 0; /* Unknonw t2 ? was 0 */ 253 hdr->reserved[6] = 0; /* Unknonw t3 ? was 1 */ 254 hdr->reserved[7] = 0; /* Unused ? */ 255 message (" Board Id: %s", board_id); 256 message (" Region: %s", region == 1 ? "World Wide (WW)" 257 : (region == 2 ? "North America (NA)" : "Unknown")); 258 259 /* Copy the trx file, calculating the checksum as we go */ 260 netgear_checksum_init (&chk_part); 261 netgear_checksum_init (&chk_whole); 262 while (!feof (kern_fp)) { 263 len = fread (buf, 1, BUF_LEN, kern_fp); 264 if (len < 1) { 265 break; 266 } 267 if (fwrite (buf, len, 1, out_fp) != 1) { 268 fatal_error (errno, "Write error"); 269 } 270 hdr->kernel_len += len; 271 netgear_checksum_add (&chk_part, (unsigned char *)buf, len); 272 netgear_checksum_add (&chk_whole, (unsigned char *)buf, len); 273 } 274 hdr->kernel_chksum = netgear_checksum_fini (&chk_part); 275 message (" Kernel Len: %u", hdr->kernel_len); 276 message ("Kernel Checksum: 0x%08x", hdr->kernel_chksum); 277 hdr->kernel_len = htonl (hdr->kernel_len); 278 hdr->kernel_chksum = htonl (hdr->kernel_chksum); 279 280 /* Now copy the root fs, calculating the checksum as we go */ 281 if (fs_fp) { 282 netgear_checksum_init (&chk_part); 283 while (!feof (fs_fp)) { 284 len = fread (buf, 1, BUF_LEN, fs_fp); 285 if (len < 1) { 286 break; 287 } 288 if (fwrite (buf, len, 1, out_fp) != 1) { 289 fatal_error (errno, "Write error"); 290 } 291 hdr->rootfs_len += len; 292 netgear_checksum_add (&chk_part, (unsigned char *)buf, len); 293 netgear_checksum_add (&chk_whole, (unsigned char *)buf, len); 294 } 295 hdr->rootfs_chksum = (netgear_checksum_fini (&chk_part)); 296 message (" Rootfs Len: %u", hdr->rootfs_len); 297 message ("Rootfs Checksum: 0x%08x", hdr->rootfs_chksum); 298 hdr->rootfs_len = htonl (hdr->rootfs_len); 299 hdr->rootfs_chksum = htonl (hdr->rootfs_chksum); 300 } 301 302 /* Calcautate the image checksum */ 303 hdr->image_chksum = netgear_checksum_fini (&chk_whole); 304 message (" Image Checksum: 0x%08x", hdr->image_chksum); 305 hdr->image_chksum = htonl (hdr->image_chksum); 306 307 /* Calculate the header checksum */ 308 netgear_checksum_init (&chk_part); 309 netgear_checksum_add (&chk_part, (unsigned char *)hdr, 310 sizeof (struct chk_header)); 311 netgear_checksum_add (&chk_part, (unsigned char *)board_id, 312 strlen (board_id)); 313 hdr->header_chksum = htonl (netgear_checksum_fini (&chk_part)); 314 315 /* Finally rewind the output and write headers */ 316 rewind (out_fp); 317 if (fwrite (hdr, sizeof (struct chk_header), 1, out_fp) != 1) { 318 fatal_error (errno, "Cannot write header"); 319 } 320 if (fwrite (board_id, strlen (board_id), 1, out_fp) != 1) { 321 fatal_error (errno, "Cannot write board id"); 322 } 323 324 /* Success */ 325 return EXIT_SUCCESS; 326} 327 328