1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <regex.h>
29 #include <devfsadm.h>
30 #include <stdio.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <ctype.h>
35 #include <sys/mc.h>
36 #include <bsm/devalloc.h>
37 
38 extern int system_labeled;
39 
40 static int lp(di_minor_t minor, di_node_t node);
41 static int serial_dialout(di_minor_t minor, di_node_t node);
42 static int serial(di_minor_t minor, di_node_t node);
43 static int diskette(di_minor_t minor, di_node_t node);
44 static int vt00(di_minor_t minor, di_node_t node);
45 static int kdmouse(di_minor_t minor, di_node_t node);
46 static int bmc(di_minor_t minor, di_node_t node);
47 static int smbios(di_minor_t minor, di_node_t node);
48 static int agp_process(di_minor_t minor, di_node_t node);
49 static int drm_node(di_minor_t minor, di_node_t node);
50 static int mc_node(di_minor_t minor, di_node_t node);
51 static int xsvc(di_minor_t minor, di_node_t node);
52 static int ucode(di_minor_t minor, di_node_t node);
53 
54 static devfsadm_create_t misc_cbt[] = {
55 	{ "vt00", "ddi_display", NULL,
56 	    TYPE_EXACT, ILEVEL_0,	vt00
57 	},
58 	{ "drm", "ddi_display:drm", NULL,
59 	    TYPE_EXACT, ILEVEL_0,	drm_node
60 	},
61 	{ "mouse", "ddi_mouse", "mouse8042",
62 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse
63 	},
64 	{ "pseudo", "ddi_pseudo", "bmc",
65 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, bmc,
66 	},
67 	{ "pseudo", "ddi_pseudo", "smbios",
68 	    TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios,
69 	},
70 	{ "disk",  "ddi_block:diskette", NULL,
71 	    TYPE_EXACT, ILEVEL_1, diskette
72 	},
73 	{ "parallel",  "ddi_printer", NULL,
74 	    TYPE_EXACT, ILEVEL_1, lp
75 	},
76 	{ "serial", "ddi_serial:mb", NULL,
77 	    TYPE_EXACT, ILEVEL_1, serial
78 	},
79 	{ "serial",  "ddi_serial:dialout,mb", NULL,
80 	    TYPE_EXACT, ILEVEL_1, serial_dialout
81 	},
82 	{ "agp", "ddi_agp:pseudo", NULL,
83 	    TYPE_EXACT, ILEVEL_0, agp_process
84 	},
85 	{ "agp", "ddi_agp:target", NULL,
86 	    TYPE_EXACT, ILEVEL_0, agp_process
87 	},
88 	{ "agp", "ddi_agp:cpugart", NULL,
89 	    TYPE_EXACT, ILEVEL_0, agp_process
90 	},
91 	{ "agp", "ddi_agp:master", NULL,
92 	    TYPE_EXACT, ILEVEL_0, agp_process
93 	},
94 	{ "pseudo", "ddi_pseudo", NULL,
95 	    TYPE_EXACT, ILEVEL_0, xsvc
96 	},
97 	{ "memory-controller", "ddi_mem_ctrl", NULL,
98 	    TYPE_EXACT, ILEVEL_0, mc_node
99 	},
100 	{ "pseudo", "ddi_pseudo", "ucode",
101 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ucode,
102 	}
103 };
104 
105 DEVFSADM_CREATE_INIT_V0(misc_cbt);
106 
107 static char *debug_mid = "misc_mid";
108 
109 typedef enum {
110 	DRIVER_AGPPSEUDO = 0,
111 	DRIVER_AGPTARGET,
112 	DRIVER_CPUGART,
113 	DRIVER_AGPMASTER_DRM,
114 	DRIVER_AGPMASTER_VGATEXT,
115 	DRIVER_UNKNOWN
116 } driver_defs_t;
117 
118 typedef struct {
119 	char	*driver_name;
120 	int	index;
121 } driver_name_table_entry_t;
122 
123 static driver_name_table_entry_t driver_name_table[] = {
124 	{ "agpgart",		DRIVER_AGPPSEUDO },
125 	{ "agptarget",		DRIVER_AGPTARGET },
126 	{ "amd64_gart",		DRIVER_CPUGART },
127 	/* AGP master device managed by drm driver */
128 	{ "i915",		DRIVER_AGPMASTER_DRM },
129 	{ "vgatext",		DRIVER_AGPMASTER_VGATEXT },
130 	{ NULL,			DRIVER_UNKNOWN }
131 };
132 
133 static devfsadm_enumerate_t agptarget_rules[1] =
134 	{ "^agp$/^agptarget([0-9]+)$", 1, MATCH_ALL };
135 static devfsadm_enumerate_t cpugart_rules[1] =
136 	{ "^agp$/^cpugart([0-9]+)$", 1, MATCH_ALL };
137 static devfsadm_enumerate_t agpmaster_rules[1] =
138 	{  "^agp$/^agpmaster([0-9]+)$", 1, MATCH_ALL };
139 
140 static devfsadm_remove_t misc_remove_cbt[] = {
141 	{ "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS,
142 		ILEVEL_0, devfsadm_rm_all
143 	},
144 	{ "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT,
145 		ILEVEL_0, devfsadm_rm_all
146 	}
147 };
148 
149 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt);
150 
151 /*
152  * Handles minor node type "ddi_display", in addition to generic processing
153  * done by display().
154  *
155  * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility.
156  */
157 /* ARGSUSED */
158 int
159 vt00(di_minor_t minor, di_node_t node)
160 {
161 	(void) devfsadm_secondary_link("vt00", "fb", 0);
162 	return (DEVFSADM_CONTINUE);
163 }
164 
165 /*
166  * type=ddi_block:diskette;addr=0,0;minor=c        diskette
167  * type=ddi_block:diskette;addr=0,0;minor=c,raw    rdiskette
168  * type=ddi_block:diskette;addr1=0;minor=c diskette\A2
169  * type=ddi_block:diskette;addr1=0;minor=c,raw     rdiskette\A2
170  */
171 static int
172 diskette(di_minor_t minor, di_node_t node)
173 {
174 	int flags = 0;
175 	char *a2;
176 	char link[PATH_MAX];
177 	char *addr = di_bus_addr(node);
178 	char *mn = di_minor_name(minor);
179 
180 	if (system_labeled)
181 		flags = DA_ADD|DA_FLOPPY;
182 
183 	if (strcmp(addr, "0,0") == 0) {
184 		if (strcmp(mn, "c") == 0) {
185 			(void) devfsadm_mklink("diskette", node, minor, flags);
186 		} else if (strcmp(mn, "c,raw") == 0) {
187 			(void) devfsadm_mklink("rdiskette", node, minor, flags);
188 		}
189 
190 	}
191 
192 	if (addr[0] == '0') {
193 		if ((a2 = strchr(addr, ',')) != NULL) {
194 			a2++;
195 			if (strcmp(mn, "c") == 0) {
196 				(void) strcpy(link, "diskette");
197 				(void) strcat(link, a2);
198 				(void) devfsadm_mklink(link, node, minor,
199 				    flags);
200 			} else if (strcmp(mn, "c,raw") == 0) {
201 				(void) strcpy(link, "rdiskette");
202 				(void) strcat(link, a2);
203 				(void) devfsadm_mklink(link, node, minor,
204 				    flags);
205 			}
206 		}
207 	}
208 
209 	return (DEVFSADM_CONTINUE);
210 }
211 
212 /*
213  * type=ddi_printer;name=lp;addr=1,3bc      lp0
214  * type=ddi_printer;name=lp;addr=1,378      lp1
215  * type=ddi_printer;name=lp;addr=1,278      lp2
216  */
217 static int
218 lp(di_minor_t minor, di_node_t node)
219 {
220 	char *addr = di_bus_addr(node);
221 	char *buf;
222 	char path[PATH_MAX + 1];
223 	devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL};
224 
225 	if (strcmp(addr, "1,3bc") == 0) {
226 		(void) devfsadm_mklink("lp0", node, minor, 0);
227 
228 	} else if (strcmp(addr, "1,378") == 0) {
229 		(void) devfsadm_mklink("lp1", node, minor, 0);
230 
231 	} else if (strcmp(addr, "1,278") == 0) {
232 		(void) devfsadm_mklink("lp2", node, minor, 0);
233 	}
234 
235 	if (strcmp(di_driver_name(node), "ecpp") != 0) {
236 		return (DEVFSADM_CONTINUE);
237 	}
238 
239 	if ((buf = di_devfs_path(node)) == NULL) {
240 		return (DEVFSADM_CONTINUE);
241 	}
242 
243 	(void) snprintf(path, sizeof (path), "%s:%s",
244 	    buf, di_minor_name(minor));
245 
246 	di_devfs_path_free(buf);
247 
248 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
249 		return (DEVFSADM_CONTINUE);
250 	}
251 
252 	(void) snprintf(path, sizeof (path), "ecpp%s", buf);
253 	free(buf);
254 	(void) devfsadm_mklink(path, node, minor, 0);
255 	return (DEVFSADM_CONTINUE);
256 }
257 
258 /*
259  * type=ddi_serial:mb;minor=a      tty00
260  * type=ddi_serial:mb;minor=b      tty01
261  * type=ddi_serial:mb;minor=c      tty02
262  * type=ddi_serial:mb;minor=d      tty03
263  */
264 static int
265 serial(di_minor_t minor, di_node_t node)
266 {
267 
268 	char *mn = di_minor_name(minor);
269 	char link[PATH_MAX];
270 
271 	(void) strcpy(link, "tty");
272 	(void) strcat(link, mn);
273 	(void) devfsadm_mklink(link, node, minor, 0);
274 
275 	if (strcmp(mn, "a") == 0) {
276 		(void) devfsadm_mklink("tty00", node, minor, 0);
277 
278 	} else if (strcmp(mn, "b") == 0) {
279 		(void) devfsadm_mklink("tty01", node, minor, 0);
280 
281 	} else if (strcmp(mn, "c") == 0) {
282 		(void) devfsadm_mklink("tty02", node, minor, 0);
283 
284 	} else if (strcmp(mn, "d") == 0) {
285 		(void) devfsadm_mklink("tty03", node, minor, 0);
286 	}
287 	return (DEVFSADM_CONTINUE);
288 }
289 
290 /*
291  * type=ddi_serial:dialout,mb;minor=a,cu   ttyd0
292  * type=ddi_serial:dialout,mb;minor=b,cu   ttyd1
293  * type=ddi_serial:dialout,mb;minor=c,cu   ttyd2
294  * type=ddi_serial:dialout,mb;minor=d,cu   ttyd3
295  */
296 static int
297 serial_dialout(di_minor_t minor, di_node_t node)
298 {
299 	char *mn = di_minor_name(minor);
300 
301 	if (strcmp(mn, "a,cu") == 0) {
302 		(void) devfsadm_mklink("ttyd0", node, minor, 0);
303 		(void) devfsadm_mklink("cua0", node, minor, 0);
304 
305 	} else if (strcmp(mn, "b,cu") == 0) {
306 		(void) devfsadm_mklink("ttyd1", node, minor, 0);
307 		(void) devfsadm_mklink("cua1", node, minor, 0);
308 
309 	} else if (strcmp(mn, "c,cu") == 0) {
310 		(void) devfsadm_mklink("ttyd2", node, minor, 0);
311 		(void) devfsadm_mklink("cua2", node, minor, 0);
312 
313 	} else if (strcmp(mn, "d,cu") == 0) {
314 		(void) devfsadm_mklink("ttyd3", node, minor, 0);
315 		(void) devfsadm_mklink("cua3", node, minor, 0);
316 	}
317 	return (DEVFSADM_CONTINUE);
318 }
319 
320 static int
321 kdmouse(di_minor_t minor, di_node_t node)
322 {
323 	(void) devfsadm_mklink("kdmouse", node, minor, 0);
324 	return (DEVFSADM_CONTINUE);
325 }
326 
327 static int
328 bmc(di_minor_t minor, di_node_t node)
329 {
330 	(void) devfsadm_mklink("bmc", node, minor, 0);
331 	return (DEVFSADM_CONTINUE);
332 }
333 
334 static int
335 smbios(di_minor_t minor, di_node_t node)
336 {
337 	(void) devfsadm_mklink("smbios", node, minor, 0);
338 	return (DEVFSADM_CONTINUE);
339 }
340 
341 static int
342 agp_process(di_minor_t minor, di_node_t node)
343 {
344 	char *minor_nm, *drv_nm;
345 	char *devfspath;
346 	char *I_path, *p_path, *buf;
347 	char *name = (char *)NULL;
348 	int i, index;
349 	devfsadm_enumerate_t rules[1];
350 
351 	minor_nm = di_minor_name(minor);
352 	drv_nm = di_driver_name(node);
353 
354 	if ((minor_nm == NULL) || (drv_nm == NULL)) {
355 		return (DEVFSADM_CONTINUE);
356 	}
357 
358 	devfsadm_print(debug_mid, "agp_process: minor=%s node=%s\n",
359 	    minor_nm, di_node_name(node));
360 
361 	devfspath = di_devfs_path(node);
362 	if (devfspath == NULL) {
363 		devfsadm_print(debug_mid, "agp_process: devfspath is NULL\n");
364 		return (DEVFSADM_CONTINUE);
365 	}
366 
367 	I_path = (char *)malloc(PATH_MAX);
368 
369 	if (I_path == NULL) {
370 		di_devfs_path_free(devfspath);
371 		devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
372 		return (DEVFSADM_CONTINUE);
373 	}
374 
375 	p_path = (char *)malloc(PATH_MAX);
376 
377 	if (p_path == NULL) {
378 		devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
379 		di_devfs_path_free(devfspath);
380 		free(I_path);
381 		return (DEVFSADM_CONTINUE);
382 	}
383 
384 	(void) strlcpy(p_path, devfspath, PATH_MAX);
385 	(void) strlcat(p_path, ":", PATH_MAX);
386 	(void) strlcat(p_path, minor_nm, PATH_MAX);
387 	di_devfs_path_free(devfspath);
388 
389 	devfsadm_print(debug_mid, "agp_process: path %s\n", p_path);
390 
391 	for (i = 0; ; i++) {
392 		if ((driver_name_table[i].driver_name == NULL) ||
393 		    (strcmp(drv_nm, driver_name_table[i].driver_name) == 0)) {
394 			index = driver_name_table[i].index;
395 			break;
396 		}
397 	}
398 	switch (index) {
399 	case DRIVER_AGPPSEUDO:
400 		devfsadm_print(debug_mid,
401 		    "agp_process: psdeudo driver name\n");
402 		name = "agpgart";
403 		(void) snprintf(I_path, PATH_MAX, "%s", name);
404 		devfsadm_print(debug_mid,
405 		    "mklink %s -> %s\n", I_path, p_path);
406 
407 		(void) devfsadm_mklink(I_path, node, minor, 0);
408 
409 		free(I_path);
410 		free(p_path);
411 		return (DEVFSADM_CONTINUE);
412 	case DRIVER_AGPTARGET:
413 		devfsadm_print(debug_mid,
414 		    "agp_process: target driver name\n");
415 		rules[0] = agptarget_rules[0];
416 		name = "agptarget";
417 		break;
418 	case DRIVER_CPUGART:
419 		devfsadm_print(debug_mid,
420 		    "agp_process: cpugart driver name\n");
421 		rules[0] = cpugart_rules[0];
422 		name = "cpugart";
423 		break;
424 	case DRIVER_AGPMASTER_DRM:
425 	case DRIVER_AGPMASTER_VGATEXT:
426 		devfsadm_print(debug_mid,
427 		    "agp_process: agpmaster driver name\n");
428 		rules[0] = agpmaster_rules[0];
429 		name = "agpmaster";
430 		break;
431 	case DRIVER_UNKNOWN:
432 		devfsadm_print(debug_mid,
433 		    "agp_process: unknown driver name=%s\n", drv_nm);
434 		free(I_path);
435 		free(p_path);
436 		return (DEVFSADM_CONTINUE);
437 	}
438 
439 	if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) {
440 		devfsadm_print(debug_mid, "agp_process: exit/coninue\n");
441 		free(I_path);
442 		free(p_path);
443 		return (DEVFSADM_CONTINUE);
444 	}
445 
446 
447 	(void) snprintf(I_path, PATH_MAX, "agp/%s%s", name, buf);
448 
449 	devfsadm_print(debug_mid, "agp_process: p_path=%s buf=%s\n",
450 	    p_path, buf);
451 
452 	free(buf);
453 
454 	devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path);
455 
456 	(void) devfsadm_mklink(I_path, node, minor, 0);
457 
458 	free(p_path);
459 	free(I_path);
460 
461 	return (DEVFSADM_CONTINUE);
462 }
463 
464 static int
465 drm_node(di_minor_t minor, di_node_t node)
466 {
467 	char *minor_nm, *drv_nm;
468 	char *devfspath;
469 	char *I_path, *p_path, *buf;
470 	char *name = "card";
471 
472 	devfsadm_enumerate_t drm_rules[1] = {"^dri$/^card([0-9]+)$", 1,
473 		MATCH_ALL };
474 
475 
476 	minor_nm = di_minor_name(minor);
477 	drv_nm = di_driver_name(node);
478 	if ((minor_nm == NULL) || (drv_nm == NULL)) {
479 		return (DEVFSADM_CONTINUE);
480 	}
481 
482 	devfsadm_print(debug_mid, "drm_node: minor=%s node=%s type=%s\n",
483 	    minor_nm, di_node_name(node), di_minor_nodetype(minor));
484 
485 	devfspath = di_devfs_path(node);
486 	if (devfspath == NULL) {
487 		devfsadm_print(debug_mid, "drm_node: devfspath is NULL\n");
488 		return (DEVFSADM_CONTINUE);
489 	}
490 
491 	I_path = (char *)malloc(PATH_MAX);
492 
493 	if (I_path == NULL) {
494 		di_devfs_path_free(devfspath);
495 		devfsadm_print(debug_mid,  "drm_node: malloc failed\n");
496 		return (DEVFSADM_CONTINUE);
497 	}
498 
499 	p_path = (char *)malloc(PATH_MAX);
500 
501 	if (p_path == NULL) {
502 		devfsadm_print(debug_mid,  "drm_node: malloc failed\n");
503 		di_devfs_path_free(devfspath);
504 		free(I_path);
505 		return (DEVFSADM_CONTINUE);
506 	}
507 
508 	(void) strlcpy(p_path, devfspath, PATH_MAX);
509 	(void) strlcat(p_path, ":", PATH_MAX);
510 	(void) strlcat(p_path, minor_nm, PATH_MAX);
511 	di_devfs_path_free(devfspath);
512 
513 	devfsadm_print(debug_mid, "drm_node: p_path %s\n", p_path);
514 
515 	if (devfsadm_enumerate_int(p_path, 0, &buf, drm_rules, 1)) {
516 		free(p_path);
517 		devfsadm_print(debug_mid, "drm_node: exit/coninue\n");
518 		return (DEVFSADM_CONTINUE);
519 	}
520 	(void) snprintf(I_path, PATH_MAX, "dri/%s%s", name, buf);
521 
522 	devfsadm_print(debug_mid, "drm_node: p_path=%s buf=%s\n",
523 	    p_path, buf);
524 
525 	free(buf);
526 
527 	devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path);
528 	(void) devfsadm_mklink(I_path, node, minor, 0);
529 
530 	free(p_path);
531 	free(I_path);
532 
533 	return (0);
534 }
535 
536 /*
537  * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd
538  */
539 static int
540 mc_node(di_minor_t minor, di_node_t node)
541 {
542 	const char *minorname = di_minor_name(minor);
543 	const char *busaddr = di_bus_addr(node);
544 	char linkpath[PATH_MAX];
545 	int unitaddr;
546 	char *c;
547 
548 	if (minorname == NULL || busaddr == NULL)
549 		return (DEVFSADM_CONTINUE);
550 
551 	errno = 0;
552 	unitaddr = strtol(busaddr, &c, 16);
553 
554 	if (errno != 0 || unitaddr < MC_AMD_DEV_OFFSET)
555 		return (DEVFSADM_CONTINUE);
556 
557 	(void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u",
558 	    unitaddr - MC_AMD_DEV_OFFSET);
559 	(void) devfsadm_mklink(linkpath, node, minor, 0);
560 	return (DEVFSADM_CONTINUE);
561 }
562 
563 /*
564  * Creates \M0 devlink for xsvc node
565  */
566 static int
567 xsvc(di_minor_t minor, di_node_t node)
568 {
569 	char *mn;
570 
571 	if (strcmp(di_node_name(node), "xsvc") != 0)
572 		return (DEVFSADM_CONTINUE);
573 
574 	mn = di_minor_name(minor);
575 	if (mn == NULL)
576 		return (DEVFSADM_CONTINUE);
577 
578 	(void) devfsadm_mklink(mn, node, minor, 0);
579 	return (DEVFSADM_CONTINUE);
580 }
581 
582 /*
583  *	/dev/ucode	->	/devices/pseudo/ucode@0:ucode
584  */
585 static int
586 ucode(di_minor_t minor, di_node_t node)
587 {
588 	char *mn;
589 
590 	(void) devfsadm_mklink("ucode", node, minor, 0);
591 	return (DEVFSADM_CONTINUE);
592 }
593