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