1*e992f068Schristos /* $NetBSD: ncdcs.c,v 1.8 2024/06/29 16:36:18 christos Exp $ */
216dce513Schristos /*
316dce513Schristos ** Program to prepare an ELF file for download to an IBM Network
416dce513Schristos ** Station 300 or 1000 running the NCD firmware.
516dce513Schristos **
616dce513Schristos ** This may also work for other loaders based on NCD code.
716dce513Schristos **
816dce513Schristos ** The program expects a signature and some marker fields a couple
916dce513Schristos ** bytes into the image itself, where the checksum gets patched in.
1016dce513Schristos ** The checksum itself is a 16bit CRC over the ELF header, the ELF
1116dce513Schristos ** program header, and the image portion of the ELF file. The start
1216dce513Schristos ** value for the CRC is 0xffff, and no final xor is performed.
1316dce513Schristos **
1416dce513Schristos ** Usage: ncdcs <infile> [outfile]
1516dce513Schristos ** e.g. ncdcs zImage zImage.ncd
1616dce513Schristos **
1716dce513Schristos ** Copyright 2002 Jochen Roth
1816dce513Schristos ** NetBSD changes copyright 2003 John Gordon
1916dce513Schristos **
2016dce513Schristos ** This program is free software; you can redistribute it and/or
2116dce513Schristos ** modify it under the terms of the GNU General Public License,
2216dce513Schristos ** Version 2, as published by the Free Software Foundation.
2316dce513Schristos **
2416dce513Schristos */
2516dce513Schristos
2616dce513Schristos
2716dce513Schristos #include <stdio.h>
2816dce513Schristos #include <stdlib.h>
2916dce513Schristos #include <string.h>
3016dce513Schristos #include <sys/types.h>
3116dce513Schristos #include <sys/wait.h>
3216dce513Schristos #include <sys/stat.h>
3316dce513Schristos #include <unistd.h>
3416dce513Schristos #include <fcntl.h>
3516dce513Schristos #include <errno.h>
3616dce513Schristos
3716dce513Schristos #ifdef __NetBSD__
3816dce513Schristos
3916dce513Schristos unsigned short crc16(unsigned short crc, unsigned char *buf, unsigned len);
4016dce513Schristos unsigned short get_be_16(unsigned char *p);
4116dce513Schristos unsigned long get_be_32(unsigned char *p);
4216dce513Schristos void put_be_16(unsigned char *p, unsigned short x);
4316dce513Schristos void put_be_32(unsigned char *p, unsigned long x);
4416dce513Schristos
4516dce513Schristos #endif
4616dce513Schristos
4716dce513Schristos static const unsigned short crc16_table[256] = {
4816dce513Schristos 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
4916dce513Schristos 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
5016dce513Schristos 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
5116dce513Schristos 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
5216dce513Schristos 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
5316dce513Schristos 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
5416dce513Schristos 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
5516dce513Schristos 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
5616dce513Schristos 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
5716dce513Schristos 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
5816dce513Schristos 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
5916dce513Schristos 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
6016dce513Schristos 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
6116dce513Schristos 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
6216dce513Schristos 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
6316dce513Schristos 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
6416dce513Schristos 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
6516dce513Schristos 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
6616dce513Schristos 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
6716dce513Schristos 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
6816dce513Schristos 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
6916dce513Schristos 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
7016dce513Schristos 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
7116dce513Schristos 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
7216dce513Schristos 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
7316dce513Schristos 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
7416dce513Schristos 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
7516dce513Schristos 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
7616dce513Schristos 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
7716dce513Schristos 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
7816dce513Schristos 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
7916dce513Schristos 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
8016dce513Schristos };
8116dce513Schristos
8216dce513Schristos
crc16(unsigned short crc,unsigned char * buf,unsigned len)8316dce513Schristos unsigned short crc16(unsigned short crc, unsigned char *buf, unsigned len)
8416dce513Schristos {
8516dce513Schristos while(len--) {
8616dce513Schristos crc = crc16_table[(crc ^ *buf++) & 0xff] ^ (crc >> 8);
8716dce513Schristos }
8816dce513Schristos return(crc);
8916dce513Schristos }
9016dce513Schristos
9116dce513Schristos
get_be_16(unsigned char * p)9216dce513Schristos unsigned short get_be_16(unsigned char *p)
9316dce513Schristos {
9416dce513Schristos unsigned short x = 0;
9516dce513Schristos x = (unsigned short)p[0];
9616dce513Schristos x = (x<<8) + (unsigned short)p[1];
9716dce513Schristos return(x);
9816dce513Schristos }
9916dce513Schristos
10016dce513Schristos
get_be_32(unsigned char * p)10116dce513Schristos unsigned long get_be_32(unsigned char *p)
10216dce513Schristos {
10316dce513Schristos unsigned long x = 0;
10416dce513Schristos x = (unsigned long)p[0];
10516dce513Schristos x = (x<<8) + (unsigned long)p[1];
10616dce513Schristos x = (x<<8) + (unsigned long)p[2];
10716dce513Schristos x = (x<<8) + (unsigned long)p[3];
10816dce513Schristos return(x);
10916dce513Schristos }
11016dce513Schristos
11116dce513Schristos
put_be_16(unsigned char * p,unsigned short x)11216dce513Schristos void put_be_16(unsigned char *p, unsigned short x)
11316dce513Schristos {
11416dce513Schristos p[0] = (x >> 8);
11516dce513Schristos p[1] = x;
11616dce513Schristos }
11716dce513Schristos
11816dce513Schristos
put_be_32(unsigned char * p,unsigned long x)11916dce513Schristos void put_be_32(unsigned char *p, unsigned long x)
12016dce513Schristos {
12116dce513Schristos p[0] = (x >> 24);
12216dce513Schristos p[1] = (x >> 16);
12316dce513Schristos p[2] = (x >> 8);
12416dce513Schristos p[3] = x;
12516dce513Schristos }
12616dce513Schristos
12716dce513Schristos
main(int argc,char * argv[])12816dce513Schristos int main(int argc, char *argv[])
12916dce513Schristos {
13016dce513Schristos char *infile, *outfile;
13116dce513Schristos struct stat st;
13216dce513Schristos unsigned char *buf, *image, *p;
13316dce513Schristos int fd, filesize, old_filesize;
13416dce513Schristos unsigned long hdr_len, img_len, img_offset;
13516dce513Schristos unsigned short crc, old_crc;
13616dce513Schristos
13716dce513Schristos /* check arguments */
13816dce513Schristos
13916dce513Schristos if(argc < 2 || argc > 3 || argv[1][0] == '-') {
14016dce513Schristos printf("usage: %s <elf-image> [ncd-image]\n", argv[0]);
14116dce513Schristos return(1);
14216dce513Schristos }
14316dce513Schristos
14416dce513Schristos infile = argv[1];
14516dce513Schristos outfile = argc > 2 ? argv[2] : NULL;
14616dce513Schristos
14716dce513Schristos /* determine file size, allocate buffer, open and read elf file */
14816dce513Schristos
14916dce513Schristos if ((fd = open(infile, O_RDONLY)) == -1) {
15016dce513Schristos perror(infile);
15116dce513Schristos return(-1);
15216dce513Schristos }
15316dce513Schristos
15416dce513Schristos if (fstat(fd, &st) == -1) {
15516dce513Schristos perror(infile);
15616dce513Schristos close(fd);
15716dce513Schristos return(-1);
15816dce513Schristos }
15916dce513Schristos buf = malloc(st.st_size + 4);
16016dce513Schristos if (buf == NULL) {
16116dce513Schristos perror(infile);
16216dce513Schristos close(fd);
16316dce513Schristos return(-1);
16416dce513Schristos }
16516dce513Schristos filesize = read(fd, buf, st.st_size);
16616dce513Schristos if (filesize != st.st_size) {
16716dce513Schristos perror(infile);
16816dce513Schristos close(fd);
16916dce513Schristos return(-1);
17016dce513Schristos }
17116dce513Schristos close(fd);
17216dce513Schristos
17316dce513Schristos /* verify elf header */
17416dce513Schristos
17516dce513Schristos if(memcmp(buf, "\177ELF", 4)) {
17616dce513Schristos fprintf(stderr, "%s: not an ELF file\n", infile);
17716dce513Schristos return(-1);
17816dce513Schristos }
17916dce513Schristos if(buf[0x04] != 1 || buf[0x05] != 2 || get_be_16(buf+0x12) != 20) {
18016dce513Schristos fprintf(stderr, "%s: not a 32bit big-endian PPC ELF file\n", infile);
18116dce513Schristos return(-1);
18216dce513Schristos }
18316dce513Schristos if(get_be_32(buf+0x1c) != 0x34) {
18416dce513Schristos fprintf(stderr, "%s: ELF PPC program header not at expected offset\n", infile);
18516dce513Schristos return(-1);
18616dce513Schristos }
18716dce513Schristos
18816dce513Schristos if((img_offset = get_be_32(buf+0x38)) != 0x10000) {
18916dce513Schristos /*
19016dce513Schristos * Doesn't seem to bother the device when the header is less
19116dce513Schristos * than 64K, and the default builds for NetBSD always generate
19216dce513Schristos * images that are not padded to a 64K boundary...
19316dce513Schristos */
19416dce513Schristos #ifndef __NetBSD__
19516dce513Schristos fprintf(stderr, "warning: %s: size of ELF header is not 64k\n", infile);
19616dce513Schristos #endif
19716dce513Schristos }
19816dce513Schristos
19916dce513Schristos /* verify checksum area markers */
20016dce513Schristos
20116dce513Schristos image = buf + img_offset;
20216dce513Schristos if(memcmp(image + 0x10, "XncdPPC", 8)) {
20316dce513Schristos fprintf(stderr, "%s: missing XncdPPC marker\n", infile);
20416dce513Schristos return(-1);
20516dce513Schristos }
20616dce513Schristos
20716dce513Schristos /* zero out the checksum and size fields, then compute checksum */
20816dce513Schristos
20916dce513Schristos p = image + 0x18;
21016dce513Schristos old_crc = get_be_16(p);
21116dce513Schristos old_filesize = get_be_32(p+4);
21216dce513Schristos
21316dce513Schristos memset(p, 0, 8);
21416dce513Schristos
21516dce513Schristos hdr_len = get_be_16(buf+0x28) + get_be_16(buf+0x2a);
21616dce513Schristos img_len = get_be_32(buf+0x44);
21716dce513Schristos
21816dce513Schristos crc = 0xffff;
21916dce513Schristos crc = crc16(crc, buf, hdr_len);
22016dce513Schristos crc = crc16(crc, buf + img_offset, img_len);
22116dce513Schristos
22216dce513Schristos printf("%s: hdr_len=0x%x img_offset=0x%x img_len=0x%x crc=0x%04x fsz=0x%06x\n", infile, (int) hdr_len, (int) img_offset, (int) img_len, crc, filesize);
22316dce513Schristos
22416dce513Schristos if(outfile) {
22516dce513Schristos /* patch in the crc and the total file size (???) */
22616dce513Schristos
22716dce513Schristos put_be_16(p, crc);
22816dce513Schristos put_be_32(p+4, filesize);
22916dce513Schristos
23016dce513Schristos if((fd = open(outfile, O_RDWR|O_CREAT|O_TRUNC, st.st_mode)) < 0) {
23116dce513Schristos perror(outfile);
23216dce513Schristos return(-1);
23316dce513Schristos }
23416dce513Schristos
23516dce513Schristos if(write(fd, buf, filesize) != filesize) {
23616dce513Schristos perror(outfile);
23716dce513Schristos return(-1);
23816dce513Schristos }
23916dce513Schristos close(fd);
24016dce513Schristos } else {
24116dce513Schristos /* Check the crc and filesize fields in the file */
24216dce513Schristos
24316dce513Schristos if((crc == old_crc) && (filesize == old_filesize)) {
24416dce513Schristos printf("%s: NCD crc and filesize seem ok.\n", infile);
24516dce513Schristos } else {
24616dce513Schristos printf("%s: NCD current crc=0x%04x fsz=0x%06x\n", infile, old_crc, old_filesize);
24716dce513Schristos }
24816dce513Schristos }
24916dce513Schristos
25016dce513Schristos return(0);
25116dce513Schristos }
252