10324 topo_usb_parse_port_type() gets value check wrong
1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2018, Joyent, Inc.
14 */
15
16 /*
17 * This module parses the private file format used for describing
18 * platform-specific USB overrides.
19 *
20 * FILE FORMAT
21 * -----------
22 *
23 * A USB topology file contains a series of lines which are separated by new
24 * lines. Leading and trailing whitespace on a line are ignored and empty lines
25 * are ignored as well. The '#' character is used as a comment character. There
26 * are a series of keywords that are supported which are used to indicate
27 * different control aspects. These keywords are all treated in a
28 * case-insensitive fashion. There are both top-level keywords and keywords that
29 * are only accepted within the context of a scope.
30 *
31 * Top-level keywords
32 * ------------------
33 *
34 * The following keywords are accepted, but must not be found inside a nested
35 * scope:
36 *
37 * 'disable-acpi' Disables the use of ACPI for this platform. This
38 * includes getting information about the port's
39 * type and visibility. This implies
40 * 'disable-acpi-match'.
41 *
42 * 'disable-acpi-match' Disables the act of trying to match ports based
43 * on ACPI.
44 *
45 *
46 * 'enable-acpi-match' Explicitly enables ACPI port matching on the
47 * platform based on ACPI.
48 *
49 * 'enable-metadata-match' Enables port matching based on metadata. This is
50 * most commonly used on platforms that have ehci
51 * and xhci controllers that share ports.
52 *
53 * 'port' Begins a port stanza that describes a single
54 * physical port. This stanza will continue until
55 * the 'end-port' keyword is encountered.
56 *
57 * Port Keywords
58 * -------------
59 *
60 * Some port keywords take arguments and others do not. When an argument exists,
61 * will occur on the subsequent line. Ports have a series of directives that
62 * describe metadata as well as directives that describe how to determine the
63 * port.
64 *
65 * 'label' Indicates that the next line contains the
66 * human-readable label for the port.
67 *
68 * 'chassis' Indicates that this port is part of the chassis
69 * and should not be enumerated elsewhere.
70 *
71 * 'external' Indicates that this port is externally visible.
72 *
73 * 'internal' Indicates that this port is internal to the
74 * chassis and cannot be accessed without opening
75 * the chassis.
76 *
77 * 'port-type' Indicates that the next line contains a number
78 * which corresponds to the type of the port. The
79 * port numbers are based on the ACPI table and
80 * may be in either base 10 or hexadecimal.
81 *
82 * 'acpi-path' Indicates that the next line contains an ACPI
83 * based name that matches the port.
84 *
85 * 'end-port' Closes the port-clause.
86 */
87
88 #include <libnvpair.h>
89 #include <sys/types.h>
90 #include <sys/stat.h>
91 #include <fcntl.h>
92 #include <fm/topo_list.h>
93 #include <fm/topo_mod.h>
94 #include <stdio.h>
95 #include <string.h>
96 #include <strings.h>
97 #include <libnvpair.h>
98 #include <sys/debug.h>
99 #include <ctype.h>
100 #include <unistd.h>
101
102 #include "topo_usb.h"
103 #include "topo_usb_int.h"
104
105 /*
106 * Maximum number of characters we expect to encounter in a line.
107 */
108 #define TOPO_USB_META_LINE_MAX 1000
109
110 /*
111 * This constant is the default set of flags that we'd like to apply when there
112 * is no configuration file present to determine the desired behavior. If one is
113 * present, we always defer to what it asks for.
114 *
115 * It's a difficult decision to enable ACPI by default or not. Unfortunately,
116 * we've encountered some systems where the ACPI information is wrong. However,
117 * we've encountered a larger number where it is correct. When it's correct,
118 * this greatly simplifies some of the work that we have to do. Our default
119 * disposition at the moment is to opt to decide its correct as that ends up
120 * giving us much better information.
121 */
122 #define USB_TOPO_META_DEFAULT_FLAGS TOPO_USB_M_ACPI_MATCH
123
124 typedef enum {
125 TOPO_USB_P_START,
126 TOPO_USB_P_PORT,
127 TOPO_USB_P_LABEL,
128 TOPO_USB_P_PORT_TYPE,
129 TOPO_USB_P_ACPI_PATH
130 } topo_usb_parse_state_t;
131
132 typedef struct topo_usb_parse {
133 topo_usb_parse_state_t tp_state;
134 topo_list_t *tp_ports;
135 topo_usb_meta_port_t *tp_cport;
136 topo_usb_meta_flags_t tp_flags;
137 } topo_usb_parse_t;
138
139 /*
140 * Read the next line in the file with content. Trim trailing and leading
141 * whitespace and trim comments out. If this results in an empty line, read the
142 * next. Returns zero if we hit EOF. Otherwise, returns one if data, or negative
143 * one if an error occurred.
144 */
145 static int
146 topo_usb_getline(topo_mod_t *mod, char *buf, size_t len, FILE *f, char **first)
147 {
148 while (fgets(buf, len, f) != NULL) {
149 char *c;
150 size_t i;
151
152 if ((c = strrchr(buf, '\n')) == NULL) {
153 topo_mod_dprintf(mod, "failed to find new line in "
154 "metadata file");
155 return (-1);
156 }
157
158 while (isspace(*c) != 0 && c >= buf) {
159 *c = '\0';
160 c--;
161 continue;
162 }
163
164 if ((c = strchr(buf, '#')) != 0) {
165 *c = '\0';
166 }
167
168 for (i = 0; buf[i] != '\0'; i++) {
169 if (isspace(buf[i]) == 0)
170 break;
171 }
172
173 if (buf[i] == '\0')
174 continue;
175 *first = &buf[i];
176 return (1);
177 }
178
179 return (0);
180 }
181
182 static boolean_t
183 topo_usb_parse_start(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
184 {
185 topo_usb_meta_port_t *port;
186
187 VERIFY3S(parse->tp_state, ==, TOPO_USB_P_START);
188 VERIFY3P(parse->tp_cport, ==, NULL);
189
190 if (strcasecmp(line, "disable-acpi") == 0) {
191 parse->tp_flags |= TOPO_USB_M_NO_ACPI;
192 parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH;
193 return (B_TRUE);
194 } else if (strcasecmp(line, "disable-acpi-match") == 0) {
195 parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH;
196 return (B_TRUE);
197 } else if (strcasecmp(line, "enable-acpi-match") == 0) {
198 parse->tp_flags |= TOPO_USB_M_ACPI_MATCH;
199 return (B_TRUE);
200 } else if (strcasecmp(line, "enable-metadata-match") == 0) {
201 parse->tp_flags |= TOPO_USB_M_METADATA_MATCH;
202 return (B_TRUE);
203 } else if (strcasecmp(line, "port") != 0) {
204 topo_mod_dprintf(mod, "expected 'port', encountered %s",
205 line);
206 return (B_FALSE);
207 }
208
209 if ((port = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_t))) ==
210 NULL) {
211 topo_mod_dprintf(mod, "failed to allocate metadata port");
212 return (B_FALSE);
213 }
214 port->tmp_port_type = 0xff;
215
216 parse->tp_cport = port;
217 parse->tp_state = TOPO_USB_P_PORT;
218 return (B_TRUE);
219 }
220
221 static boolean_t
222 topo_usb_parse_port(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
223 {
224 VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT);
225 VERIFY3P(parse->tp_cport, !=, NULL);
226
227 if (strcasecmp(line, "label") == 0) {
228 parse->tp_state = TOPO_USB_P_LABEL;
229 } else if (strcasecmp(line, "chassis") == 0) {
230 parse->tp_cport->tmp_flags |= TOPO_USB_F_CHASSIS;
231 } else if (strcasecmp(line, "external") == 0) {
232 parse->tp_cport->tmp_flags |= TOPO_USB_F_EXTERNAL;
233 } else if (strcasecmp(line, "internal") == 0) {
234 parse->tp_cport->tmp_flags |= TOPO_USB_F_INTERNAL;
235 } else if (strcasecmp(line, "port-type") == 0) {
236 parse->tp_state = TOPO_USB_P_PORT_TYPE;
237 } else if (strcasecmp(line, "acpi-path") == 0) {
238 parse->tp_state = TOPO_USB_P_ACPI_PATH;
239 } else if (strcasecmp(line, "end-port") == 0) {
240 topo_list_append(parse->tp_ports, parse->tp_cport);
241 parse->tp_cport = NULL;
242 parse->tp_state = TOPO_USB_P_START;
243 } else {
244 topo_mod_dprintf(mod, "illegal directive in port block: %s",
245 line);
246 return (B_FALSE);
247 }
248
249 return (B_TRUE);
250 }
251
252 static boolean_t
253 topo_usb_parse_label(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
254 {
255 size_t i, len;
256
257 VERIFY3S(parse->tp_state, ==, TOPO_USB_P_LABEL);
258
259 len = strlen(line);
260 for (i = 0; i < len; i++) {
261 if (isascii(line[i]) == 0 || isprint(line[i]) == 0) {
262 topo_mod_dprintf(mod, "label character %llu is "
263 "invalid: 0x%x", i, line[i]);
264 return (B_FALSE);
265 }
266 }
267
268 if (parse->tp_cport->tmp_label != NULL) {
269 topo_mod_strfree(mod, parse->tp_cport->tmp_label);
270 }
271
272 if ((parse->tp_cport->tmp_label = topo_mod_strdup(mod, line)) == NULL) {
273 topo_mod_dprintf(mod, "failed to duplicate label for port");
274 return (B_FALSE);
275 }
276
277 parse->tp_state = TOPO_USB_P_PORT;
278
279 return (B_TRUE);
280 }
281
282 static boolean_t
283 topo_usb_parse_port_type(topo_mod_t *mod, topo_usb_parse_t *parse,
284 const char *line)
285 {
286 unsigned long val;
287 char *eptr;
288
289 VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT_TYPE);
290
291 errno = 0;
292 val = strtoul(line, &eptr, 0);
293 if (errno != 0 || *eptr != '\0' || val > UINT_MAX) {
294 topo_mod_dprintf(mod, "encountered bad value for port-type "
295 "line: %s", line);
296 return (B_FALSE);
297 }
298
299 parse->tp_cport->tmp_port_type = (uint_t)val;
300
301 parse->tp_state = TOPO_USB_P_PORT;
302 return (B_TRUE);
303 }
304
305 static boolean_t
306 topo_usb_parse_path(topo_mod_t *mod, topo_usb_parse_t *parse,
307 topo_usb_path_type_t ptype, const char *line)
308 {
309 char *fspath;
310 topo_usb_meta_port_path_t *path;
311
312 VERIFY(parse->tp_state == TOPO_USB_P_ACPI_PATH);
313 VERIFY3P(parse->tp_cport, !=, NULL);
314
315 if ((fspath = topo_mod_strdup(mod, line)) == NULL) {
316 topo_mod_dprintf(mod, "failed to duplicate path");
317 return (B_FALSE);
318 }
319
320 if ((path = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_path_t))) ==
321 NULL) {
322 topo_mod_dprintf(mod, "failed to allocate meta port path "
323 "structure");
324 topo_mod_strfree(mod, fspath);
325 return (B_FALSE);
326 }
327
328 path->tmpp_type = ptype;
329 path->tmpp_path = fspath;
330
331 topo_list_append(&parse->tp_cport->tmp_paths, path);
332
333 parse->tp_state = TOPO_USB_P_PORT;
334 return (B_TRUE);
335 }
336
337
338 void
339 topo_usb_free_metadata(topo_mod_t *mod, topo_list_t *metadata)
340 {
341 topo_usb_meta_port_t *mp;
342
343 while ((mp = topo_list_next(metadata)) != NULL) {
344 topo_usb_meta_port_path_t *path;
345
346 while ((path = topo_list_next((&mp->tmp_paths))) != NULL) {
347 topo_list_delete(&mp->tmp_paths, path);
348 topo_mod_strfree(mod, path->tmpp_path);
349 topo_mod_free(mod, path,
350 sizeof (topo_usb_meta_port_path_t));
351 }
352
353 topo_list_delete(metadata, mp);
354 topo_mod_strfree(mod, mp->tmp_label);
355 topo_mod_free(mod, mp, sizeof (topo_usb_meta_port_t));
356 }
357 }
358
359 int
360 topo_usb_load_metadata(topo_mod_t *mod, tnode_t *pnode, topo_list_t *list,
361 topo_usb_meta_flags_t *flagsp)
362 {
363 int fd;
364 FILE *f = NULL;
365 char buf[TOPO_USB_META_LINE_MAX], *first, *prod;
366 int ret;
367 topo_usb_parse_t parse;
368 char pbuf[PATH_MAX];
369
370 *flagsp = USB_TOPO_META_DEFAULT_FLAGS;
371
372 /*
373 * If no product string, just leave it as is and don't attempt to get
374 * metadata.
375 */
376 if ((topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
377 FM_FMRI_AUTH_PRODUCT, &prod, &ret)) != 0) {
378 topo_mod_dprintf(mod, "skipping metadata load: failed to get "
379 "auth");
380 return (0);
381 }
382
383 if (snprintf(pbuf, sizeof (pbuf), "maps/%s-usb.usbtopo", prod) >=
384 sizeof (pbuf)) {
385 topo_mod_dprintf(mod, "skipping metadata load: product name "
386 "too long");
387 topo_mod_strfree(mod, prod);
388 return (0);
389 }
390 topo_mod_strfree(mod, prod);
391
392 if ((fd = topo_mod_file_search(mod, pbuf, O_RDONLY)) < 0) {
393 topo_mod_dprintf(mod, "skipping metadata load: couldn't find "
394 "%s", pbuf);
395 return (0);
396 }
397
398
399 if ((f = fdopen(fd, "r")) == NULL) {
400 topo_mod_dprintf(mod, "failed to fdopen metadata file %s: %s",
401 pbuf, strerror(errno));
402 VERIFY0(close(fd));
403 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
404 goto err;
405 }
406
407 bzero(&parse, sizeof (parse));
408 parse.tp_ports = list;
409 parse.tp_state = TOPO_USB_P_START;
410
411 while ((ret = topo_usb_getline(mod, buf, sizeof (buf), f, &first)) !=
412 0) {
413 if (ret == -1) {
414 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
415 goto err;
416 }
417
418 switch (parse.tp_state) {
419 case TOPO_USB_P_START:
420 if (!topo_usb_parse_start(mod, &parse, first)) {
421 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
422 goto err;
423 }
424 break;
425 case TOPO_USB_P_PORT:
426 if (!topo_usb_parse_port(mod, &parse, first)) {
427 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
428 goto err;
429 }
430 break;
431 case TOPO_USB_P_LABEL:
432 if (!topo_usb_parse_label(mod, &parse, first)) {
433 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
434 goto err;
435 }
436 break;
437 case TOPO_USB_P_PORT_TYPE:
438 if (!topo_usb_parse_port_type(mod, &parse, first)) {
439 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
440 goto err;
441 }
442 break;
443
444 case TOPO_USB_P_ACPI_PATH:
445 if (!topo_usb_parse_path(mod, &parse, TOPO_USB_T_ACPI,
446 first)) {
447 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
448 goto err;
449 }
450 break;
451 }
452 }
453
454 if (parse.tp_state != TOPO_USB_P_START) {
455 topo_mod_dprintf(mod, "metadata file didn't end in correct "
456 "state, failing");
457 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
458 goto err;
459 }
460
461 topo_mod_dprintf(mod, "successfully loaded metadata %s", pbuf);
462 VERIFY0(fclose(f));
463 *flagsp = parse.tp_flags;
464 return (0);
465
466 err:
467 if (f != NULL)
468 VERIFY0(fclose(f));
469 topo_usb_free_metadata(mod, list);
470 return (ret);
471 }
--- EOF ---