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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2018 Joyent, Inc.  All rights reserved.
25  */
26 
27 #include <regex.h>
28 #include <devfsadm.h>
29 #include <stdio.h>
30 #include <strings.h>
31 #include <stdlib.h>
32 #include <limits.h>
33 #include <ctype.h>
34 #include <sys/mc_amd.h>
35 #include <bsm/devalloc.h>
36 
37 extern int system_labeled;
38 
39 static int ln_minor_name(di_minor_t minor, di_node_t node);
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 ipmi(di_minor_t minor, di_node_t node);
47 static int mc_node(di_minor_t minor, di_node_t node);
48 
49 static devfsadm_create_t misc_cbt[] = {
50 	{ "vt00", "ddi_display", NULL,
51 	    TYPE_EXACT, ILEVEL_0,	vt00
52 	},
53 	{ "mouse", "ddi_mouse", "mouse8042",
54 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse
55 	},
56 	{ "pseudo", "ddi_pseudo", "ipmi",
57 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi,
58 	},
59 	{ "pseudo", "ddi_pseudo", "smbios",
60 	    TYPE_EXACT | DRV_EXACT, ILEVEL_1, ln_minor_name,
61 	},
62 	/* floppies share the same class, but not link regex, as hard disks */
63 	{ "disk",  "ddi_block:diskette", NULL,
64 	    TYPE_EXACT, ILEVEL_1, diskette
65 	},
66 	{ "parallel",  "ddi_printer", NULL,
67 	    TYPE_EXACT, ILEVEL_1, lp
68 	},
69 	{ "serial", "ddi_serial:mb", NULL,
70 	    TYPE_EXACT, ILEVEL_1, serial
71 	},
72 	{ "serial",  "ddi_serial:dialout,mb", NULL,
73 	    TYPE_EXACT, ILEVEL_1, serial_dialout
74 	},
75 	{ "pseudo", "ddi_pseudo", "xsvc",
76 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name,
77 	},
78 	{ "pseudo", "ddi_pseudo", "srn",
79 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name,
80 	},
81 	{ "memory-controller", "ddi_mem_ctrl", NULL,
82 	    TYPE_EXACT, ILEVEL_0, mc_node
83 	},
84 	{ "pseudo", "ddi_pseudo", "ucode",
85 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name,
86 	},
87 };
88 
89 DEVFSADM_CREATE_INIT_V0(misc_cbt);
90 
91 static devfsadm_remove_t misc_remove_cbt[] = {
92 	{ "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS,
93 		ILEVEL_0, devfsadm_rm_all
94 	},
95 	{ "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT,
96 		ILEVEL_0, devfsadm_rm_all
97 	},
98 	{ "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE,
99 		ILEVEL_0, devfsadm_rm_all
100 	},
101 	{ "disk", "^(diskette|rdiskette)([0-9]*)$",
102 		RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all
103 	},
104 	{ "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE,
105 		ILEVEL_1, devfsadm_rm_all
106 	},
107 	{ "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE,
108 		ILEVEL_1, devfsadm_rm_all
109 	},
110 	{ "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE,
111 		ILEVEL_1, devfsadm_rm_all
112 	}
113 };
114 
115 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt);
116 
117 /*
118  * Any /dev/foo entry named after the minor name such as
119  * /devices/.../driver@0:foo
120  */
121 static int
122 ln_minor_name(di_minor_t minor, di_node_t node)
123 {
124 	(void) devfsadm_mklink(di_minor_name(minor), node, minor, 0);
125 	return (DEVFSADM_CONTINUE);
126 }
127 
128 /*
129  * Handles minor node type "ddi_display", in addition to generic processing
130  * done by display().
131  *
132  * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility.
133  */
134 /* ARGSUSED */
135 int
136 vt00(di_minor_t minor, di_node_t node)
137 {
138 	(void) devfsadm_secondary_link("vt00", "fb", 0);
139 	return (DEVFSADM_CONTINUE);
140 }
141 
142 /*
143  * type=ddi_block:diskette;addr=0,0;minor=c        diskette
144  * type=ddi_block:diskette;addr=0,0;minor=c,raw    rdiskette
145  * type=ddi_block:diskette;addr1=0;minor=c diskette\A2
146  * type=ddi_block:diskette;addr1=0;minor=c,raw     rdiskette\A2
147  */
148 static int
149 diskette(di_minor_t minor, di_node_t node)
150 {
151 	int flags = 0;
152 	char *a2;
153 	char link[PATH_MAX];
154 	char *addr = di_bus_addr(node);
155 	char *mn = di_minor_name(minor);
156 
157 	if (system_labeled)
158 		flags = DA_ADD|DA_FLOPPY;
159 
160 	if (strcmp(addr, "0,0") == 0) {
161 		if (strcmp(mn, "c") == 0) {
162 			(void) devfsadm_mklink("diskette", node, minor, flags);
163 		} else if (strcmp(mn, "c,raw") == 0) {
164 			(void) devfsadm_mklink("rdiskette", node, minor, flags);
165 		}
166 
167 	}
168 
169 	if (addr[0] == '0') {
170 		if ((a2 = strchr(addr, ',')) != NULL) {
171 			a2++;
172 			if (strcmp(mn, "c") == 0) {
173 				(void) strcpy(link, "diskette");
174 				(void) strcat(link, a2);
175 				(void) devfsadm_mklink(link, node, minor,
176 				    flags);
177 			} else if (strcmp(mn, "c,raw") == 0) {
178 				(void) strcpy(link, "rdiskette");
179 				(void) strcat(link, a2);
180 				(void) devfsadm_mklink(link, node, minor,
181 				    flags);
182 			}
183 		}
184 	}
185 
186 	return (DEVFSADM_CONTINUE);
187 }
188 
189 /*
190  * type=ddi_printer;name=lp;addr=1,3bc      lp0
191  * type=ddi_printer;name=lp;addr=1,378      lp1
192  * type=ddi_printer;name=lp;addr=1,278      lp2
193  */
194 static int
195 lp(di_minor_t minor, di_node_t node)
196 {
197 	char *addr = di_bus_addr(node);
198 	char *buf;
199 	char path[PATH_MAX + 1];
200 	devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL};
201 
202 	if (strcmp(addr, "1,3bc") == 0) {
203 		(void) devfsadm_mklink("lp0", node, minor, 0);
204 
205 	} else if (strcmp(addr, "1,378") == 0) {
206 		(void) devfsadm_mklink("lp1", node, minor, 0);
207 
208 	} else if (strcmp(addr, "1,278") == 0) {
209 		(void) devfsadm_mklink("lp2", node, minor, 0);
210 	}
211 
212 	if (strcmp(di_driver_name(node), "ecpp") != 0) {
213 		return (DEVFSADM_CONTINUE);
214 	}
215 
216 	if ((buf = di_devfs_path(node)) == NULL) {
217 		return (DEVFSADM_CONTINUE);
218 	}
219 
220 	(void) snprintf(path, sizeof (path), "%s:%s",
221 	    buf, di_minor_name(minor));
222 
223 	di_devfs_path_free(buf);
224 
225 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
226 		return (DEVFSADM_CONTINUE);
227 	}
228 
229 	(void) snprintf(path, sizeof (path), "ecpp%s", buf);
230 	free(buf);
231 	(void) devfsadm_mklink(path, node, minor, 0);
232 	return (DEVFSADM_CONTINUE);
233 }
234 
235 /*
236  * type=ddi_serial:mb;minor=a      tty00
237  * type=ddi_serial:mb;minor=b      tty01
238  * type=ddi_serial:mb;minor=c      tty02
239  * type=ddi_serial:mb;minor=d      tty03
240  */
241 static int
242 serial(di_minor_t minor, di_node_t node)
243 {
244 
245 	char *mn = di_minor_name(minor);
246 	char link[PATH_MAX];
247 
248 	(void) strcpy(link, "tty");
249 	(void) strcat(link, mn);
250 	(void) devfsadm_mklink(link, node, minor, 0);
251 
252 	if (strcmp(mn, "a") == 0) {
253 		(void) devfsadm_mklink("tty00", node, minor, 0);
254 
255 	} else if (strcmp(mn, "b") == 0) {
256 		(void) devfsadm_mklink("tty01", node, minor, 0);
257 
258 	} else if (strcmp(mn, "c") == 0) {
259 		(void) devfsadm_mklink("tty02", node, minor, 0);
260 
261 	} else if (strcmp(mn, "d") == 0) {
262 		(void) devfsadm_mklink("tty03", node, minor, 0);
263 	}
264 	return (DEVFSADM_CONTINUE);
265 }
266 
267 /*
268  * type=ddi_serial:dialout,mb;minor=a,cu   ttyd0
269  * type=ddi_serial:dialout,mb;minor=b,cu   ttyd1
270  * type=ddi_serial:dialout,mb;minor=c,cu   ttyd2
271  * type=ddi_serial:dialout,mb;minor=d,cu   ttyd3
272  */
273 static int
274 serial_dialout(di_minor_t minor, di_node_t node)
275 {
276 	char *mn = di_minor_name(minor);
277 
278 	if (strcmp(mn, "a,cu") == 0) {
279 		(void) devfsadm_mklink("ttyd0", node, minor, 0);
280 		(void) devfsadm_mklink("cua0", node, minor, 0);
281 
282 	} else if (strcmp(mn, "b,cu") == 0) {
283 		(void) devfsadm_mklink("ttyd1", node, minor, 0);
284 		(void) devfsadm_mklink("cua1", node, minor, 0);
285 
286 	} else if (strcmp(mn, "c,cu") == 0) {
287 		(void) devfsadm_mklink("ttyd2", node, minor, 0);
288 		(void) devfsadm_mklink("cua2", node, minor, 0);
289 
290 	} else if (strcmp(mn, "d,cu") == 0) {
291 		(void) devfsadm_mklink("ttyd3", node, minor, 0);
292 		(void) devfsadm_mklink("cua3", node, minor, 0);
293 	}
294 	return (DEVFSADM_CONTINUE);
295 }
296 
297 static int
298 kdmouse(di_minor_t minor, di_node_t node)
299 {
300 	(void) devfsadm_mklink("kdmouse", node, minor, 0);
301 	return (DEVFSADM_CONTINUE);
302 }
303 
304 static int
305 ipmi(di_minor_t minor, di_node_t node)
306 {
307 	/*
308 	 * Follow convention from other systems, and include an instance#,
309 	 * even though there will only be one.
310 	 */
311 	(void) devfsadm_mklink("ipmi0", node, minor, 0);
312 	return (DEVFSADM_CONTINUE);
313 }
314 
315 /*
316  * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd
317  */
318 static int
319 mc_node(di_minor_t minor, di_node_t node)
320 {
321 	const char *minorname = di_minor_name(minor);
322 	const char *busaddr = di_bus_addr(node);
323 	char linkpath[PATH_MAX];
324 	int unitaddr;
325 	char *c;
326 
327 	if (minorname == NULL || busaddr == NULL)
328 		return (DEVFSADM_CONTINUE);
329 
330 	errno = 0;
331 	unitaddr = strtol(busaddr, &c, 16);
332 
333 	if (errno != 0)
334 		return (DEVFSADM_CONTINUE);
335 
336 	if (unitaddr == 0) {
337 		(void) snprintf(linkpath, sizeof (linkpath), "mc/mc");
338 	} else if (unitaddr >= MC_AMD_DEV_OFFSET) {
339 		(void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u",
340 		    unitaddr - MC_AMD_DEV_OFFSET);
341 	} else {
342 		(void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u",
343 		    minor->dev_minor);
344 	}
345 	(void) devfsadm_mklink(linkpath, node, minor, 0);
346 	return (DEVFSADM_CONTINUE);
347 }
348