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 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 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 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 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 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 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 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 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 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 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 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