1 /*
2  * Copyright © 2015 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #include <regex.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "intel_reg_spec.h"
34 
35 static const struct port_desc port_descs[] = {
36 	{
37 		.name = "mmio",
38 		.port = PORT_MMIO,
39 		.stride = 4,
40 	},
41 	{
42 		.name = "portio-vga",
43 		.port = PORT_PORTIO_VGA,
44 		.stride = 1,
45 	},
46 	{
47 		.name = "mmio-vga",
48 		.port = PORT_MMIO_VGA,
49 		.stride = 1,
50 	},
51 	{
52 		.name = "bunit",
53 		.port = PORT_BUNIT,
54 		.stride = 1,
55 	},
56 	{
57 		.name = "punit",
58 		.port = PORT_PUNIT,
59 		.stride = 1,
60 	},
61 	{
62 		.name = "nc",
63 		.port = PORT_NC,
64 		.stride = 4,
65 	},
66 	{
67 		.name = "dpio",
68 		.port = PORT_DPIO,
69 		.stride = 4,
70 	},
71 	{
72 		.name = "gpio-nc",
73 		.port = PORT_GPIO_NC,
74 		.stride = 4,
75 	},
76 	{
77 		.name = "gpio_nc",
78 		.port = PORT_GPIO_NC,
79 		.stride = 4,
80 	},
81 	{
82 		.name = "cck",
83 		.port = PORT_CCK,
84 		.stride = 1,
85 	},
86 	{
87 		.name = "ccu",
88 		.port = PORT_CCU,
89 		.stride = 4,
90 	},
91 	{
92 		.name = "dpio2",
93 		.port = PORT_DPIO2,
94 		.stride = 4,
95 	},
96 	{
97 		.name = "flisdsi",
98 		.port = PORT_FLISDSI,
99 		.stride = 1,
100 	},
101 };
102 
103 /*
104  * Parse port desc of the form (PORTNAME|PORTNUM|MMIO-OFFSET) into reg. NULL or
105  * zero length s is regarded as MMIO.
106  */
parse_port_desc(struct reg * reg,const char * s)107 int parse_port_desc(struct reg *reg, const char *s)
108 {
109 	enum port_addr port = PORT_NONE;
110 	int i;
111 
112 	if (s && *s) {
113 		/* See if port is specified by number. */
114 		char *endp;
115 		unsigned long n = strtoul(s, &endp, 16);
116 		if (endp > s && *endp == 0) {
117 			if (n > PORT_MAX) {
118 				/* Not a sideband port, assume MMIO offset. */
119 				port = PORT_MMIO;
120 				reg->mmio_offset = n;
121 			} else {
122 				port = n;
123 				reg->mmio_offset = 0;
124 			}
125 		} else {
126 			reg->mmio_offset = 0;
127 		}
128 	} else {
129 		/* No port, default to searching for MMIO. */
130 		port = PORT_MMIO;
131 		reg->mmio_offset = 0;
132 	}
133 
134 	for (i = 0; i < ARRAY_SIZE(port_descs); i++) {
135 		if ((port != PORT_NONE && port_descs[i].port == port) ||
136 		    (s && strcasecmp(s, port_descs[i].name) == 0)) {
137 			reg->port_desc = port_descs[i];
138 			return 0;
139 		}
140 	}
141 
142 	return -1;
143 }
144 
skip_space(const char * line)145 static const char *skip_space(const char *line)
146 {
147 	while (*line && isspace(*line))
148 		line++;
149 
150 	return line;
151 }
152 
ignore_line(const char * line)153 static bool ignore_line(const char *line)
154 {
155 	line = skip_space(line);
156 
157 	switch (*line) {
158 	case '\0':
159 	case '#':
160 	case ';':
161 		return true;
162 	case '/':
163 		return *(line + 1) == '/';
164 	}
165 
166 	return false;
167 }
168 
include_file(const char * line,const char * source)169 static char *include_file(const char *line, const char *source)
170 {
171 	char *filename, *p;
172 
173 	line = skip_space(line);
174 	if (*line == '(')
175 		return NULL;
176 
177 	/* this'll be plenty */
178 	filename = malloc(strlen(source) + strlen(line) + 1);
179 	if (!filename)
180 		return NULL;
181 
182 	p = strrchr(source, '/');
183 	if (p && *line != '/') {
184 		int len = p - source + 1;
185 
186 		memcpy(filename, source, len);
187 		strcpy(filename + len, line);
188 	} else {
189 		strcpy(filename, line);
190 	}
191 
192 	p = strchr(filename, '\n');
193 	if (p)
194 		*p = '\0';
195 
196 	return filename;
197 }
198 
199 #define SPC	"[[:space:]]*"
200 #define SEP	SPC "," SPC
201 #define BEG	"^" SPC "\\(" SPC
202 #define END	SPC "\\)" SPC "$"
203 #define VALUE	"([[:print:]]*)"
204 #define QVALUE	"'" VALUE "'"
205 #define REGEXP	BEG QVALUE SEP QVALUE SEP QVALUE END
206 
parse_line(struct reg * reg,const char * line)207 static int parse_line(struct reg *reg, const char *line)
208 {
209 	static regex_t regex;
210 	static bool initialized = false;
211 	regmatch_t match[4];
212 	int i, ret;
213 
214 	if (!initialized) {
215 		if (regcomp (&regex, REGEXP, REG_EXTENDED)) {
216 			fprintf(stderr, "regcomp %s\n", REGEXP);
217 			return -1;
218 		}
219 		initialized = true;
220 	}
221 
222 	ret = regexec(&regex, line, ARRAY_SIZE(match), match, 0);
223 	if (ret)
224 		ret = -1;
225 
226 	for (i = 1; i < ARRAY_SIZE(match) && ret == 0; i++) {
227 		char *p, *e;
228 
229 		p = strndup(line + match[i].rm_so,
230 			    match[i].rm_eo - match[i].rm_so);
231 
232 		if (i == 1) {
233 			reg->name = p;
234 		} else if (i == 2) {
235 			reg->addr = strtoul(p, &e, 16);
236 			free(p);
237 			if (*e)
238 				ret = -1;
239 		} else if (i == 3) {
240 			ret = parse_port_desc(reg, p);
241 			free(p);
242 		}
243 	}
244 
245 	if (ret)
246 		free(reg->name);
247 
248 	return ret;
249 }
250 
parse_file(struct reg ** regs,size_t * nregs,ssize_t index,const char * filename)251 static ssize_t parse_file(struct reg **regs, size_t *nregs,
252 			  ssize_t index, const char *filename)
253 {
254 	FILE *file;
255 	char *line = NULL, *include;
256 	size_t linesize = 0;
257 	int lineno = 0, r;
258 	ssize_t ret = -1;
259 
260 	file = fopen(filename, "r");
261 	if (!file) {
262 		fprintf(stderr, "Error: fopen '%s': %s\n",
263 			filename, strerror(errno));
264 		return -1;
265 	}
266 
267 	while (getline(&line, &linesize, file) != -1) {
268 		struct reg reg = {};
269 
270 		lineno++;
271 
272 		if (ignore_line(line))
273 			continue;
274 
275 		include = include_file(line, filename);
276 		if (include) {
277 			index = parse_file(regs, nregs, index, include);
278 			free(include);
279 			if (index < 0) {
280 				fprintf(stderr, "Error: %s:%d: %s",
281 					filename, lineno, line);
282 				goto out;
283 			}
284 			continue;
285 		}
286 
287 		r = parse_line(&reg, line);
288 		if (r < 0) {
289 			fprintf(stderr, "Error: %s:%d: %s",
290 				filename, lineno, line);
291 			goto out;
292 		} else if (r) {
293 			continue;
294 		}
295 
296 		if (!*regs || index >= *nregs) {
297 			if (!*regs)
298 				*nregs = 64;
299 			else
300 				*nregs *= 2;
301 
302 			*regs = recalloc(*regs, *nregs, sizeof(**regs));
303 			if (!*regs) {
304 				fprintf(stderr, "Error: %s\n", strerror(ENOMEM));
305 				goto out;
306 			}
307 		}
308 
309 		(*regs)[index++] = reg;
310 	}
311 
312 	ret = index;
313 
314 out:
315 	free(line);
316 	fclose(file);
317 
318 	return ret;
319 }
320 
321 /*
322  * Get register definitions from file.
323  */
intel_reg_spec_file(struct reg ** regs,const char * file)324 ssize_t intel_reg_spec_file(struct reg **regs, const char *file)
325 {
326 	size_t nregs = 0;
327 	*regs = NULL;
328 
329 	return parse_file(regs, &nregs, 0, file);
330 }
331 
332 /*
333  * Free the memory allocated for register definitions.
334  */
intel_reg_spec_free(struct reg * regs,size_t n)335 void intel_reg_spec_free(struct reg *regs, size_t n)
336 {
337 	size_t i;
338 
339 	for (i = 0; i < n; i++) {
340 		free(regs[i].name);
341 	}
342 	free(regs);
343 }
344 
intel_reg_spec_print_ports(void)345 void intel_reg_spec_print_ports(void)
346 {
347 	int i;
348 
349 	for (i = 0; i < ARRAY_SIZE(port_descs); i++)
350 		printf("%s%s", i == 0 ? "" : ", ", port_descs[i].name);
351 }
352