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__FBSDID("$FreeBSD$");
35
36#include <sys/types.h>
37#include <sys/queue.h>
38#include <sys/socket.h>
39#include <net/if.h>
40#include <stdlib.h>
41#include <stddef.h>
42#include <unistd.h>
43#include <stdio.h>
44#include <errno.h>
45#include <string.h>
46#include <libgen.h>
47#include <err.h>
48#include <ctype.h>
49
50#include <compat/ndis/pe_var.h>
51
52#include "inf.h"
53
54static int insert_padding(void **, int *);
55extern const char *__progname;
56
57/*
58 * Sections within Windows PE files are defined using virtual
59 * and physical address offsets and virtual and physical sizes.
60 * The physical values define how the section data is stored in
61 * the executable file while the virtual values describe how the
62 * sections will look once loaded into memory. It happens that
63 * the linker in the Microsoft(r) DDK will tend to generate
64 * binaries where the virtual and physical values are identical,
65 * which means in most cases we can just transfer the file
66 * directly to memory without any fixups. This is not always
67 * the case though, so we have to be prepared to handle files
68 * where the in-memory section layout differs from the disk file
69 * section layout.
70 *
71 * There are two kinds of variations that can occur: the relative
72 * virtual address of the section might be different from the
73 * physical file offset, and the virtual section size might be
74 * different from the physical size (for example, the physical
75 * size of the .data section might be 1024 bytes, but the virtual
76 * size might be 1384 bytes, indicating that the data section should
77 * actually use up 1384 bytes in RAM and be padded with zeros). What we
78 * do is read the original file into memory and then make an in-memory
79 * copy with all of the sections relocated, re-sized and zero padded
80 * according to the virtual values specified in the section headers.
81 * We then emit the fixed up image file for use by the if_ndis driver.
82 * This way, we don't have to do the fixups inside the kernel.
83 */
84
85#define ROUND_DOWN(n, align)    (((uintptr_t)n) & ~((align) - 1l))
86#define ROUND_UP(n, align)      ROUND_DOWN(((uintptr_t)n) + (align) - 1l, \
87                                (align))
88
89#define SET_HDRS(x)	\
90	dos_hdr = (image_dos_header *)x;				\
91	nt_hdr = (image_nt_header *)(x + dos_hdr->idh_lfanew);		\
92	sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
93
94static int
95insert_padding(void **imgbase, int *imglen)
96{
97        image_section_header	*sect_hdr;
98        image_dos_header	*dos_hdr;
99        image_nt_header		*nt_hdr;
100	image_optional_header	opt_hdr;
101        int			i = 0, sections, curlen = 0;
102	int			offaccum = 0, oldraddr, oldrlen;
103	uint8_t			*newimg, *tmp;
104
105	newimg = malloc(*imglen);
106
107	if (newimg == NULL)
108		return(ENOMEM);
109
110	bcopy(*imgbase, newimg, *imglen);
111	curlen = *imglen;
112
113	if (pe_get_optional_header((vm_offset_t)newimg, &opt_hdr))
114		return(0);
115
116        sections = pe_numsections((vm_offset_t)newimg);
117
118	SET_HDRS(newimg);
119
120	for (i = 0; i < sections; i++) {
121		oldraddr = sect_hdr->ish_rawdataaddr;
122		oldrlen = sect_hdr->ish_rawdatasize;
123		sect_hdr->ish_rawdataaddr = sect_hdr->ish_vaddr;
124		offaccum += ROUND_UP(sect_hdr->ish_vaddr - oldraddr,
125		    opt_hdr.ioh_filealign);
126		offaccum +=
127		    ROUND_UP(sect_hdr->ish_misc.ish_vsize,
128			     opt_hdr.ioh_filealign) -
129		    ROUND_UP(sect_hdr->ish_rawdatasize,
130			     opt_hdr.ioh_filealign);
131		tmp = realloc(newimg, *imglen + offaccum);
132		if (tmp == NULL) {
133			free(newimg);
134			return(ENOMEM);
135		}
136		newimg = tmp;
137		SET_HDRS(newimg);
138		sect_hdr += i;
139		bzero(newimg + sect_hdr->ish_rawdataaddr,
140		    ROUND_UP(sect_hdr->ish_misc.ish_vsize,
141		    opt_hdr.ioh_filealign));
142		bcopy((uint8_t *)(*imgbase) + oldraddr,
143		    newimg + sect_hdr->ish_rawdataaddr, oldrlen);
144		sect_hdr++;
145	}
146
147	free(*imgbase);
148
149	*imgbase = newimg;
150	*imglen += offaccum;
151
152	return(0);
153}
154
155static void
156usage(void)
157{
158	fprintf(stderr, "Usage: %s [-O] [-i <inffile>] -s <sysfile> "
159	    "[-n devname] [-o outfile]\n", __progname);
160	fprintf(stderr, "       %s -f <firmfile>\n", __progname);
161
162	exit(1);
163}
164
165static void
166bincvt(char *sysfile, char *outfile, void *img, int fsize)
167{
168	char			*ptr;
169	char			tname[] = "/tmp/ndiscvt.XXXXXX";
170	char			sysbuf[1024];
171	FILE			*binfp;
172
173	mkstemp(tname);
174
175	binfp = fopen(tname, "a+");
176	if (binfp == NULL)
177		err(1, "opening %s failed", tname);
178
179	if (fwrite(img, fsize, 1, binfp) != 1)
180		err(1, "failed to output binary image");
181
182	fclose(binfp);
183
184	outfile = strdup(basename(outfile));
185	if (strchr(outfile, '.'))
186		*strchr(outfile, '.') = '\0';
187
188	snprintf(sysbuf, sizeof(sysbuf),
189#ifdef __i386__
190	    "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
191#endif
192#ifdef __amd64__
193	    "objcopy -I binary -O elf64-x86-64-freebsd -B i386 %s %s.o\n",
194#endif
195	    tname, outfile);
196	printf("%s", sysbuf);
197	system(sysbuf);
198	unlink(tname);
199
200	ptr = tname;
201	while (*ptr) {
202		if (*ptr == '/' || *ptr == '.')
203			*ptr = '_';
204		ptr++;
205	}
206
207	snprintf(sysbuf, sizeof(sysbuf),
208	    "objcopy --redefine-sym _binary_%s_start=ndis_%s_drv_data_start "
209	    "--strip-symbol _binary_%s_size "
210	    "--redefine-sym _binary_%s_end=ndis_%s_drv_data_end %s.o %s.o\n",
211	    tname, sysfile, tname, tname, sysfile, outfile, outfile);
212	printf("%s", sysbuf);
213	system(sysbuf);
214
215	return;
216}
217
218static void
219firmcvt(char *firmfile)
220{
221	char			*basefile, *outfile, *ptr;
222	char			sysbuf[1024];
223
224	outfile = strdup(basename(firmfile));
225	basefile = strdup(outfile);
226
227	snprintf(sysbuf, sizeof(sysbuf),
228#ifdef __i386__
229	    "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
230#endif
231#ifdef __amd64__
232	    "objcopy -I binary -O elf64-x86-64-freebsd -B i386 %s %s.o\n",
233#endif
234	    firmfile, outfile);
235	printf("%s", sysbuf);
236	system(sysbuf);
237
238	ptr = firmfile;
239	while (*ptr) {
240		if (*ptr == '/' || *ptr == '.')
241			*ptr = '_';
242		ptr++;
243	}
244	ptr = basefile;
245	while (*ptr) {
246		if (*ptr == '/' || *ptr == '.')
247			*ptr = '_';
248		else
249			*ptr = tolower(*ptr);
250		ptr++;
251	}
252
253	snprintf(sysbuf, sizeof(sysbuf),
254	    "objcopy --redefine-sym _binary_%s_start=%s_start "
255	    "--strip-symbol _binary_%s_size "
256	    "--redefine-sym _binary_%s_end=%s_end %s.o %s.o\n",
257	    firmfile, basefile, firmfile, firmfile,
258	    basefile, outfile, outfile);
259	ptr = sysbuf;
260	printf("%s", sysbuf);
261	system(sysbuf);
262
263	snprintf(sysbuf, sizeof(sysbuf),
264	    "ld -Bshareable -d -warn-common -o %s.ko %s.o\n",
265	    outfile, outfile);
266	printf("%s", sysbuf);
267	system(sysbuf);
268
269	free(basefile);
270
271	exit(0);
272}
273
274int
275main(int argc, char *argv[])
276{
277	FILE			*fp, *outfp;
278	int			i, bin = 0;
279	void			*img;
280	int			n, fsize, cnt;
281	unsigned char		*ptr;
282	char			*inffile = NULL, *sysfile = NULL;
283	char			*outfile = NULL, *firmfile = NULL;
284	char			*dname = NULL;
285	int			ch;
286
287	while((ch = getopt(argc, argv, "i:s:o:n:f:O")) != -1) {
288		switch(ch) {
289		case 'f':
290			firmfile = optarg;
291			break;
292		case 'i':
293			inffile = optarg;
294			break;
295		case 's':
296			sysfile = optarg;
297			break;
298		case 'o':
299			outfile = optarg;
300			break;
301		case 'n':
302			dname = optarg;
303			break;
304		case 'O':
305			bin = 1;
306			break;
307		default:
308			usage();
309			break;
310		}
311	}
312
313	if (firmfile != NULL)
314		firmcvt(firmfile);
315
316	if (sysfile == NULL)
317		usage();
318
319	/* Open the .SYS file and load it into memory */
320	fp = fopen(sysfile, "r");
321	if (fp == NULL)
322		err(1, "opening .SYS file '%s' failed", sysfile);
323	fseek (fp, 0L, SEEK_END);
324	fsize = ftell (fp);
325	rewind (fp);
326	img = calloc(fsize, 1);
327	n = fread (img, fsize, 1, fp);
328	if (n == 0)
329		err(1, "reading .SYS file '%s' failed", sysfile);
330
331	fclose(fp);
332
333	if (insert_padding(&img, &fsize)) {
334		fprintf(stderr, "section relocation failed\n");
335		exit(1);
336	}
337
338	if (outfile == NULL || strcmp(outfile, "-") == 0)
339		outfp = stdout;
340	else {
341		outfp = fopen(outfile, "w");
342		if (outfp == NULL)
343			err(1, "opening output file '%s' failed", outfile);
344	}
345
346	fprintf(outfp, "\n/*\n");
347	fprintf(outfp, " * Generated from %s and %s (%d bytes)\n",
348	    inffile == NULL ? "<notused>" : inffile, sysfile, fsize);
349	fprintf(outfp, " */\n\n");
350
351	if (dname != NULL) {
352		if (strlen(dname) > IFNAMSIZ)
353			err(1, "selected device name '%s' is "
354			    "too long (max chars: %d)", dname, IFNAMSIZ);
355		fprintf (outfp, "#define NDIS_DEVNAME \"%s\"\n", dname);
356		fprintf (outfp, "#define NDIS_MODNAME %s\n\n", dname);
357	}
358
359	if (inffile == NULL) {
360		fprintf (outfp, "#ifdef NDIS_REGVALS\n");
361		fprintf (outfp, "ndis_cfg ndis_regvals[] = {\n");
362        	fprintf (outfp, "\t{ NULL, NULL, { 0 }, 0 }\n");
363		fprintf (outfp, "#endif /* NDIS_REGVALS */\n");
364
365		fprintf (outfp, "};\n\n");
366	} else {
367		fp = fopen(inffile, "r");
368		if (fp == NULL)
369			err(1, "opening .INF file '%s' failed", inffile);
370
371
372		inf_parse(fp, outfp);
373		fclose(fp);
374	}
375
376	fprintf(outfp, "\n#ifdef NDIS_IMAGE\n");
377
378	if (bin) {
379		sysfile = strdup(basename(sysfile));
380		ptr = (unsigned char *)sysfile;
381		while (*ptr) {
382			if (*ptr == '.')
383				*ptr = '_';
384			ptr++;
385		}
386		fprintf(outfp,
387		    "\nextern unsigned char ndis_%s_drv_data_start[];\n",
388		    sysfile);
389		fprintf(outfp, "static unsigned char *drv_data = "
390		    "ndis_%s_drv_data_start;\n\n", sysfile);
391		bincvt(sysfile, outfile, img, fsize);
392		goto done;
393	}
394
395
396	fprintf(outfp, "\nextern unsigned char drv_data[];\n\n");
397
398	fprintf(outfp, "__asm__(\".data\");\n");
399	fprintf(outfp, "__asm__(\".globl  drv_data\");\n");
400	fprintf(outfp, "__asm__(\".type   drv_data, @object\");\n");
401	fprintf(outfp, "__asm__(\".size   drv_data, %d\");\n", fsize);
402	fprintf(outfp, "__asm__(\"drv_data:\");\n");
403
404	ptr = img;
405	cnt = 0;
406	while(cnt < fsize) {
407		fprintf (outfp, "__asm__(\".byte ");
408		for (i = 0; i < 10; i++) {
409			cnt++;
410			if (cnt == fsize) {
411				fprintf(outfp, "0x%.2X\");\n", ptr[i]);
412				goto done;
413			} else {
414				if (i == 9)
415					fprintf(outfp, "0x%.2X\");\n", ptr[i]);
416				else
417					fprintf(outfp, "0x%.2X, ", ptr[i]);
418			}
419		}
420		ptr += 10;
421	}
422
423done:
424
425	fprintf(outfp, "#endif /* NDIS_IMAGE */\n");
426
427	if (fp != NULL)
428		fclose(fp);
429	fclose(outfp);
430	free(img);
431	exit(0);
432}
433