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