1/*
2 * Copyright (c) 2003
3 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#ifdef __FreeBSD__
35__FBSDID("$FreeBSD: src/usr.sbin/ndiscvt/ndiscvt.c,v 1.9.2.2 2005/02/23 16:31:47 wpaul Exp $");
36#endif
37#ifdef __NetBSD__
38__RCSID("$NetBSD: ndiscvt.c,v 1.9 2006/05/28 11:33:56 jnemeth Exp $");
39#endif
40
41
42#include <sys/types.h>
43#ifdef __NetBSD__
44#include <sys/stdint.h>
45#endif
46#include <sys/queue.h>
47#include <sys/socket.h>
48#include <sys/lock.h>
49#include <net/if.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <stdio.h>
53#include <errno.h>
54#include <string.h>
55#include <libgen.h>
56#include <err.h>
57#include <ctype.h>
58
59#include <compat/ndis/pe_var.h>
60
61#include "inf.h"
62
63static int insert_padding(void **, int *);
64extern const char *__progname;
65
66/*
67 * Sections within Windows PE files are defined using virtual
68 * and physical address offsets and virtual and physical sizes.
69 * The physical values define how the section data is stored in
70 * the executable file while the virtual values describe how the
71 * sections will look once loaded into memory. It happens that
72 * the linker in the Microsoft(r) DDK will tend to generate
73 * binaries where the virtual and physical values are identical,
74 * which means in most cases we can just transfer the file
75 * directly to memory without any fixups. This is not always
76 * the case though, so we have to be prepared to handle files
77 * where the in-memory section layout differs from the disk file
78 * section layout.
79 *
80 * There are two kinds of variations that can occur: the relative
81 * virtual address of the section might be different from the
82 * physical file offset, and the virtual section size might be
83 * different from the physical size (for example, the physical
84 * size of the .data section might be 1024 bytes, but the virtual
85 * size might be 1384 bytes, indicating that the data section should
86 * actually use up 1384 bytes in RAM and be padded with zeros). What we
87 * do is read the original file into memory and then make an in-memory
88 * copy with all of the sections relocated, re-sized and zero padded
89 * according to the virtual values specified in the section headers.
90 * We then emit the fixed up image file for use by the if_ndis driver.
91 * This way, we don't have to do the fixups inside the kernel.
92 */
93
94#define ROUND_DOWN(n, align)    (((uintptr_t)n) & ~((align) - 1l))
95#define ROUND_UP(n, align)      ROUND_DOWN(((uintptr_t)n) + (align) - 1l, \
96                                (align))
97
98#define SET_HDRS(x)	\
99	dos_hdr = (image_dos_header *)x;				\
100	nt_hdr = (image_nt_header *)(x + dos_hdr->idh_lfanew);		\
101	sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr +	\
102	    sizeof(image_nt_header));
103
104static
105int insert_padding(imgbase, imglen)
106	void			**imgbase;
107	int			*imglen;
108{
109        image_section_header	*sect_hdr;
110        image_dos_header	*dos_hdr;
111        image_nt_header		*nt_hdr;
112	image_optional_header	opt_hdr;
113        int			i = 0, sections, curlen = 0;
114	int			offaccum = 0, oldraddr, oldrlen;
115	uint8_t			*newimg, *tmp;
116
117	newimg = malloc(*imglen);
118
119	if (newimg == NULL)
120		return(ENOMEM);
121
122	bcopy(*imgbase, newimg, *imglen);
123	curlen = *imglen;
124
125	if (pe_get_optional_header((vm_offset_t)newimg, &opt_hdr))
126		return(0);
127
128        sections = pe_numsections((vm_offset_t)newimg);
129
130	SET_HDRS(newimg);
131
132	for (i = 0; i < sections; i++) {
133		oldraddr = sect_hdr->ish_rawdataaddr;
134		oldrlen = sect_hdr->ish_rawdatasize;
135		sect_hdr->ish_rawdataaddr = sect_hdr->ish_vaddr;
136		offaccum += ROUND_UP(sect_hdr->ish_vaddr - oldraddr,
137		    opt_hdr.ioh_filealign);
138		offaccum +=
139		    ROUND_UP(sect_hdr->ish_misc.ish_vsize,
140			     opt_hdr.ioh_filealign) -
141		    ROUND_UP(sect_hdr->ish_rawdatasize,
142			     opt_hdr.ioh_filealign);
143		tmp = realloc(newimg, *imglen + offaccum);
144		if (tmp == NULL) {
145			free(newimg);
146			return(ENOMEM);
147		}
148		newimg = tmp;
149		SET_HDRS(newimg);
150		sect_hdr += i;
151		bzero(newimg + sect_hdr->ish_rawdataaddr,
152		    ROUND_UP(sect_hdr->ish_misc.ish_vsize,
153		    opt_hdr.ioh_filealign));
154		bcopy((uint8_t *)(*imgbase) + oldraddr,
155		    newimg + sect_hdr->ish_rawdataaddr, oldrlen);
156		sect_hdr++;
157	}
158
159	free(*imgbase);
160
161	*imgbase = newimg;
162	*imglen += offaccum;
163
164	return(0);
165}
166
167__dead static void
168usage(void)
169{
170	fprintf(stderr, "Usage: %s [-O] [-i <inffile>] -s <sysfile> "
171	    "[-n devname] [-o outfile]\n", __progname);
172	fprintf(stderr, "       %s -f <firmfile>\n", __progname);
173
174	exit(1);
175}
176
177static void
178bincvt(char *sysfile, char *outfile, void *img, int fsize)
179{
180	char			*ptr;
181	char			tname[] = "/tmp/ndiscvt.XXXXXX";
182	char			sysbuf[1024];
183	FILE			*binfp;
184	int			fd;
185
186	fd = mkstemp(tname);
187	if (fd == -1)
188		err(1, "creating temp file %s failed", tname);
189
190	binfp = fdopen(fd, "a+");
191	if (binfp == NULL) {
192		unlink(tname);
193		err(1, "opening %s failed", tname);
194	}
195
196	if (fwrite(img, fsize, 1, binfp) != 1) {
197		unlink(tname);
198		err(1, "failed to output binary image");
199	}
200
201	fclose(binfp);
202
203	outfile = strdup(basename(outfile));
204	if (strchr(outfile, '.'))
205		*strchr(outfile, '.') = '\0';
206
207	snprintf(sysbuf, sizeof(sysbuf),
208#ifdef __i386__
209#ifdef __FreeBSD__
210	    "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
211#else
212	    "objcopy -I binary -O elf32-i386 -B i386 %s %s.o\n",
213#endif /* __FreeBSD__ */
214#endif
215#ifdef __amd64__
216	    "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n",
217#endif
218	    tname, outfile);
219	printf("%s", sysbuf);
220	system(sysbuf);
221	unlink(tname);
222
223	ptr = tname;
224	while (*ptr) {
225		if (*ptr == '/' || *ptr == '.')
226			*ptr = '_';
227		ptr++;
228	}
229
230	snprintf(sysbuf, sizeof(sysbuf),
231	    "objcopy --redefine-sym _binary_%s_start=%s_drv_data_start "
232	    "--strip-symbol _binary_%s_size "
233	    "--redefine-sym _binary_%s_end=%s_drv_data_end %s.o %s.o\n",
234	    tname, sysfile, tname, tname, sysfile, outfile, outfile);
235	printf("%s", sysbuf);
236	system(sysbuf);
237
238	free(outfile);
239	return;
240}
241
242__dead static void
243firmcvt(char *firmfile)
244{
245	char			*basefile, *outfile, *ptr;
246	char			sysbuf[1024];
247
248	outfile = strdup(basename(firmfile));
249	basefile = strdup(outfile);
250
251	snprintf(sysbuf, sizeof(sysbuf),
252#ifdef __i386__
253#ifdef __FreeBSD__
254	    "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
255#else
256	    "objcopy -I binary -O elf32-i386 -B i386 %s %s.o\n",
257#endif /* __FreeBSD__ */
258#endif
259#ifdef __amd64__
260	    "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n",
261#endif
262	    firmfile, outfile);
263	printf("%s", sysbuf);
264	system(sysbuf);
265
266	ptr = firmfile;
267	while (*ptr) {
268		if (*ptr == '/' || *ptr == '.')
269			*ptr = '_';
270		ptr++;
271	}
272	ptr = basefile;
273	while (*ptr) {
274		if (*ptr == '/' || *ptr == '.')
275			*ptr = '_';
276		else
277			*ptr = tolower((int)*ptr);
278		ptr++;
279	}
280
281	snprintf(sysbuf, sizeof(sysbuf),
282	    "objcopy --redefine-sym _binary_%s_start=%s_start "
283	    "--strip-symbol _binary_%s_size "
284	    "--redefine-sym _binary_%s_end=%s_end %s.o %s.o\n",
285	    firmfile, basefile, firmfile, firmfile,
286	    basefile, outfile, outfile);
287	ptr = sysbuf;
288	printf("%s", sysbuf);
289	system(sysbuf);
290
291	snprintf(sysbuf, sizeof(sysbuf),
292	    "ld -Bshareable -d -warn-common -o %s.ko %s.o\n",
293	    outfile, outfile);
294	printf("%s", sysbuf);
295	system(sysbuf);
296
297	free(basefile);
298
299	exit(0);
300}
301
302int
303main(int argc, char *argv[])
304{
305	FILE			*fp, *outfp;
306	int			i, bin = 0;
307	void			*img;
308	int			n, fsize, cnt;
309	char			*ptr;
310	char			*inffile = NULL, *sysfile = NULL;
311	char			*outfile = NULL, *firmfile = NULL;
312	char			*dname = NULL;
313	int			ch;
314
315	while((ch = getopt(argc, argv, "i:s:o:n:f:O")) != -1) {
316		switch(ch) {
317		case 'f':
318			firmfile = optarg;
319			break;
320		case 'i':
321			inffile = optarg;
322			break;
323		case 's':
324			sysfile = optarg;
325			break;
326		case 'o':
327			outfile = optarg;
328			break;
329		case 'n':
330			dname = optarg;
331			break;
332		case 'O':
333			bin = 1;
334			break;
335		default:
336			usage();
337			break;
338		}
339	}
340
341	if (firmfile != NULL)
342		firmcvt(firmfile);
343
344	if (sysfile == NULL)
345		usage();
346
347	/* Open the .SYS file and load it into memory */
348	fp = fopen(sysfile, "r");
349	if (fp == NULL)
350		err(1, "opening .SYS file '%s' failed", sysfile);
351	fseek (fp, 0L, SEEK_END);
352	fsize = ftell (fp);
353	if (fsize == -1)
354		err(1, "getting size of .SYS file '%s' failed", sysfile);
355	rewind (fp);
356	img = malloc(fsize);
357	n = fread (img, fsize, 1, fp);
358	if (n < 1)
359		err(1, "reading .SYS file '%s' failed", sysfile);
360
361	fclose(fp);
362	fp = NULL;
363
364	if (insert_padding(&img, &fsize)) {
365		fprintf(stderr, "section relocation failed\n");
366		exit(1);
367	}
368
369	if (outfile == NULL || strcmp(outfile, "-") == 0)
370		outfp = stdout;
371	else {
372		outfp = fopen(outfile, "w");
373		if (outfp == NULL)
374			err(1, "opening output file '%s' failed", outfile);
375	}
376
377	fprintf(outfp, "\n/*\n");
378	fprintf(outfp, " * Generated from %s and %s (%d bytes)\n",
379	    inffile == NULL ? "<notused>" : inffile, sysfile, fsize);
380	fprintf(outfp, " */\n\n");
381
382	if (dname != NULL) {
383		if (strlen(dname) > IFNAMSIZ)
384			err(1, "selected device name '%s' is "
385			    "too long (max chars: %d)", dname, IFNAMSIZ);
386		fprintf (outfp, "#define NDIS_DEVNAME \"%s\"\n", dname);
387		fprintf (outfp, "#define NDIS_MODNAME %s\n\n", dname);
388	}
389
390	if (inffile == NULL) {
391		fprintf (outfp, "#ifdef NDIS_REGVALS\n");
392		fprintf (outfp, "ndis_cfg ndis_regvals[] = {\n");
393        	fprintf (outfp, "\t{ NULL, NULL, { 0 }, 0 }\n");
394		fprintf (outfp, "#endif /* NDIS_REGVALS */\n");
395
396		fprintf (outfp, "};\n\n");
397	} else {
398		fp = fopen(inffile, "r");
399		if (fp == NULL)
400			err(1, "opening .INF file '%s' failed", inffile);
401
402
403		inf_parse(fp, outfp);
404		fclose(fp);
405		fp = NULL;
406	}
407
408	fprintf(outfp, "\n#ifdef NDIS_IMAGE\n");
409
410	if (bin) {
411		sysfile = strdup(basename(sysfile));
412		ptr = sysfile;
413		while (*ptr) {
414			if (*ptr == '.')
415				*ptr = '_';
416			ptr++;
417		}
418		fprintf(outfp,
419		    "\nextern unsigned char %s_drv_data_start[];\n",
420		    sysfile);
421		fprintf(outfp, "static unsigned char *drv_data = "
422		    "%s_drv_data_start;\n\n", sysfile);
423		bincvt(sysfile, outfile, img, fsize);
424		goto done;
425	}
426
427
428	fprintf(outfp, "\nextern unsigned char drv_data[];\n\n");
429
430	fprintf(outfp, "__asm__(\".data\");\n");
431	fprintf(outfp, "__asm__(\".globl  drv_data\");\n");
432	fprintf(outfp, "__asm__(\".type   drv_data, @object\");\n");
433	fprintf(outfp, "__asm__(\".size   drv_data, %d\");\n", fsize);
434	fprintf(outfp, "__asm__(\"drv_data:\");\n");
435
436	ptr = img;
437	cnt = 0;
438	while(cnt < fsize) {
439		fprintf (outfp, "__asm__(\".byte ");
440		for (i = 0; i < 10; i++) {
441			cnt++;
442			if (cnt == fsize) {
443				fprintf(outfp, "0x%.2X\");\n", ptr[i]);
444				goto done;
445			} else {
446				if (i == 9)
447					fprintf(outfp, "0x%.2X\");\n", ptr[i]);
448				else
449					fprintf(outfp, "0x%.2X, ", ptr[i]);
450			}
451		}
452		ptr += 10;
453	}
454
455done:
456
457	fprintf(outfp, "#endif /* NDIS_IMAGE */\n");
458
459	if (fp != NULL)
460		fclose(fp);
461	fclose(outfp);
462	free(img);
463	exit(0);
464}
465