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