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