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