1/*-
2 * Copyright (c) 2013 Hans Petter Selasky. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26/*
27 * This utility sorts sysinit structure entries in binary format and
28 * prints out the result in C-format.
29 */
30
31#include <stdio.h>
32#include <stdint.h>
33#include <stdlib.h>
34#include <err.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <string.h>
38#include <sysexits.h>
39#include "sysinit.h"
40
41static int opt_R;
42static const char *input_f;
43static const char *output_f;
44static const char *struct_name;
45static const char *keyword;
46static struct sysinit_data **start;
47static struct sysinit_data **stop;
48
49static int input_file = -1;
50static int output_file = -1;
51
52static uint8_t *input_ptr;
53static uint32_t input_len;
54
55static uint32_t endian32;
56
57static char scratch_buf[4096];
58
59static int success;
60
61static void do_sysinit(void);
62
63/* the following function converts the numbers into host endian format */
64
65static uint32_t
66read32(uint32_t val)
67{
68	uint32_t temp;
69	uint32_t endian;
70
71	endian = endian32;
72	temp = 0;
73
74	while (val) {
75		temp |= (val & 0xF) << ((endian & 0xF) * 4);
76		endian >>= 4;
77		val >>= 4;
78	}
79	return (temp);
80}
81
82static void
83do_write(int fd, const char *buf)
84{
85	int len = strlen(buf);
86
87	if (write(fd, buf, len) != len)
88		err(EX_SOFTWARE, "Could not write to output file");
89}
90
91static void *
92do_malloc(int size)
93{
94	void *ptr;
95
96	ptr = malloc(size);
97	if (ptr == NULL)
98		errx(EX_SOFTWARE, "Could not allocate memory");
99	return (ptr);
100}
101
102static void
103usage(void)
104{
105	errx(EX_USAGE, "sysinit -i sysinit.bin -o sysinit_data.c \\\n"
106	    "\t" "-k sysinit -s sysinit_data [ -R (reverse)]");
107}
108
109static void
110cleanup(void)
111{
112	if (output_file >= 0)
113		close(output_file);
114	if (input_file >= 0)
115		close(input_file);
116	if (success == 0) {
117		if (output_f)
118			unlink(output_f);
119	}
120}
121
122static int
123compare(const void *_pa, const void *_pb)
124{
125	const struct sysinit_data * const *pa = _pa;
126	const struct sysinit_data * const *pb = _pb;
127
128	if ((*pa)->dw_msb_value > (*pb)->dw_msb_value)
129		return (1);
130
131	if ((*pa)->dw_msb_value < (*pb)->dw_msb_value)
132		return (-1);
133
134	if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value)
135		return (1);
136
137	if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value)
138		return (-1);
139
140	return (0);	/* equal */
141}
142
143static int
144compare_R(const void *_pa, const void *_pb)
145{
146	const struct sysinit_data * const *pa = _pa;
147	const struct sysinit_data * const *pb = _pb;
148
149	if ((*pa)->dw_msb_value > (*pb)->dw_msb_value)
150		return (-1);
151
152	if ((*pa)->dw_msb_value < (*pb)->dw_msb_value)
153		return (1);
154
155	if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value)
156		return (-1);
157
158	if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value)
159		return (1);
160
161	return (0);	/* equal */
162}
163
164int
165main(int argc, char **argv)
166{
167	struct sysinit_data **sipp;
168	int c;
169	int entries;
170	off_t off;
171
172	while ((c = getopt(argc, argv, "k:s:i:o:Rh")) != -1) {
173		switch (c) {
174		case 'i':
175			input_f = optarg;
176			break;
177		case 'o':
178			output_f = optarg;
179			break;
180		case 'R':
181			opt_R = 1;
182			break;
183		case 'k':
184			keyword = optarg;
185			break;
186		case 's':
187			struct_name = optarg;
188			break;
189		default:
190			usage();
191		}
192	}
193
194	if (input_f == NULL || output_f == NULL ||
195	    struct_name == NULL || keyword == NULL)
196		usage();
197
198	atexit(&cleanup);
199
200	cleanup();
201
202	input_file = open(input_f, O_RDONLY);
203	if (input_file < 0)
204		err(EX_SOFTWARE, "Could not open input file: %s", input_f);
205
206	output_file = open(output_f, O_TRUNC | O_CREAT | O_RDWR, 0600);
207	if (output_file < 0)
208		err(EX_SOFTWARE, "Could not open output file: %s", output_f);
209
210	off = lseek(input_file, 0, SEEK_END);
211
212	input_ptr = do_malloc(off);
213	input_len = off;
214
215	if (input_len % (uint32_t)sizeof(struct sysinit_data)) {
216		errx(EX_SOFTWARE, "Input file size is not divisible by %u",
217		    (unsigned int)sizeof(struct sysinit_data));
218	}
219	off = lseek(input_file, 0, SEEK_SET);
220	if (off < 0)
221		err(EX_SOFTWARE, "Could not seek to start of input file");
222
223	if (read(input_file, input_ptr, input_len) != input_len)
224		err(EX_SOFTWARE, "Could not read input file");
225
226	entries = input_len / (uint32_t)sizeof(struct sysinit_data);
227
228	start = do_malloc(sizeof(void *) * entries);
229	stop = start + entries;
230
231	for (c = 0; c != entries; c++)
232		start[c] = &((struct sysinit_data *)input_ptr)[c];
233
234	if (start != stop)
235		endian32 = (*start)->dw_endian32;
236
237	/* switch all fields to host endian order */
238	for (sipp = start; sipp < stop; sipp++) {
239		(*sipp)->dw_lsb_value = read32((*sipp)->dw_lsb_value);
240		(*sipp)->dw_msb_value = read32((*sipp)->dw_msb_value);
241		(*sipp)->dw_file_line = read32((*sipp)->dw_file_line);
242	}
243
244	if (opt_R == 0) {
245		/* sort entries, rising numerical order */
246		qsort(start, entries, sizeof(void *), &compare);
247	} else {
248		/* sort entries, falling numerical order */
249		qsort(start, entries, sizeof(void *), &compare_R);
250	}
251
252	/* safe all strings */
253	for (sipp = start; sipp < stop; sipp++) {
254		(*sipp)->b_keyword_name[sizeof((*sipp)->b_keyword_name) - 1] = 0;
255		(*sipp)->b_global_type[sizeof((*sipp)->b_global_type) - 1] = 0;
256		(*sipp)->b_global_name[sizeof((*sipp)->b_global_name) - 1] = 0;
257		(*sipp)->b_file_name[sizeof((*sipp)->b_file_name) - 1] = 0;
258		(*sipp)->b_debug_info[sizeof((*sipp)->b_debug_info) - 1] = 0;
259	}
260
261	if (strcmp(keyword, "sysinit") == 0)
262		do_sysinit();
263	else if (strcmp(keyword, "sysuninit") == 0)
264		do_sysinit();
265	else
266		errx(EX_USAGE, "Unknown keyword '%s'", keyword);
267
268	success = 1;
269
270	return (0);
271}
272
273static void
274do_sysinit(void)
275{
276	struct sysinit_data **sipp;
277	int c;
278
279	snprintf(scratch_buf, sizeof(scratch_buf),
280	    "/*\n"
281	    " * This file was automatically generated.\n"
282	    " * Please do not edit.\n"
283	    " */\n\n");
284
285	/* write out externals */
286	for (c = 0, sipp = start; sipp < stop; c++, sipp++) {
287		if (strcmp((const char *)(*sipp)->b_keyword_name, keyword))
288			continue;
289		if ((*sipp)->dw_msb_value == 0)
290			continue;
291
292		snprintf(scratch_buf, sizeof(scratch_buf),
293		    "/* #%04u: %s entry at %s:%u */\n",
294		    c, (*sipp)->b_debug_info, (*sipp)->b_file_name,
295		    (unsigned int)(*sipp)->dw_file_line);
296
297		do_write(output_file, scratch_buf);
298
299		snprintf(scratch_buf, sizeof(scratch_buf),
300		    "extern %s %s;\n\n", (*sipp)->b_global_type,
301		    (*sipp)->b_global_name);
302
303		do_write(output_file, scratch_buf);
304	}
305
306	snprintf(scratch_buf, sizeof(scratch_buf),
307	    "const void *%s[] = {\n", struct_name);
308
309	do_write(output_file, scratch_buf);
310
311	/* write out actual table */
312	for (c = 0, sipp = start; sipp < stop; c++, sipp++) {
313		if (strcmp((const char *)(*sipp)->b_keyword_name, keyword))
314			continue;
315		if ((*sipp)->dw_msb_value == 0)
316			continue;
317
318		snprintf(scratch_buf, sizeof(scratch_buf),
319		    "\t&%s, /* #%04u */\n",
320		    (*sipp)->b_global_name, (unsigned int)c);
321
322		do_write(output_file, scratch_buf);
323	}
324
325	snprintf(scratch_buf, sizeof(scratch_buf),
326	    "\t(const void *)0\n"
327	    "};\n");
328
329	do_write(output_file, scratch_buf);
330}
331