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 2012 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 lp(di_minor_t minor, di_node_t node); 40 static int serial_dialout(di_minor_t minor, di_node_t node); 41 static int serial(di_minor_t minor, di_node_t node); 42 static int diskette(di_minor_t minor, di_node_t node); 43 static int vt00(di_minor_t minor, di_node_t node); 44 static int kdmouse(di_minor_t minor, di_node_t node); 45 static int ipmi(di_minor_t minor, di_node_t node); 46 static int smbios(di_minor_t minor, di_node_t node); 47 static int agp_process(di_minor_t minor, di_node_t node); 48 static int drm_node(di_minor_t minor, di_node_t node); 49 static int mc_node(di_minor_t minor, di_node_t node); 50 static int xsvc(di_minor_t minor, di_node_t node); 51 static int srn(di_minor_t minor, di_node_t node); 52 static int ucode(di_minor_t minor, di_node_t node); 53 54 55 static devfsadm_create_t misc_cbt[] = { 56 { "vt00", "ddi_display", NULL, 57 TYPE_EXACT, ILEVEL_0, vt00 58 }, 59 { "drm", "ddi_display:drm", NULL, 60 TYPE_EXACT, ILEVEL_0, drm_node 61 }, 62 { "mouse", "ddi_mouse", "mouse8042", 63 TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse 64 }, 65 { "pseudo", "ddi_pseudo", "ipmi", 66 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi, 67 }, 68 { "pseudo", "ddi_pseudo", "smbios", 69 TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios, 70 }, 71 /* floppies share the same class, but not link regex, as hard disks */ 72 { "disk", "ddi_block:diskette", NULL, 73 TYPE_EXACT, ILEVEL_1, diskette 74 }, 75 { "parallel", "ddi_printer", NULL, 76 TYPE_EXACT, ILEVEL_1, lp 77 }, 78 { "serial", "ddi_serial:mb", NULL, 79 TYPE_EXACT, ILEVEL_1, serial 80 }, 81 { "serial", "ddi_serial:dialout,mb", NULL, 82 TYPE_EXACT, ILEVEL_1, serial_dialout 83 }, 84 { "agp", "ddi_agp:pseudo", NULL, 85 TYPE_EXACT, ILEVEL_0, agp_process 86 }, 87 { "agp", "ddi_agp:target", NULL, 88 TYPE_EXACT, ILEVEL_0, agp_process 89 }, 90 { "agp", "ddi_agp:cpugart", NULL, 91 TYPE_EXACT, ILEVEL_0, agp_process 92 }, 93 { "agp", "ddi_agp:master", NULL, 94 TYPE_EXACT, ILEVEL_0, agp_process 95 }, 96 { "pseudo", "ddi_pseudo", NULL, 97 TYPE_EXACT, ILEVEL_0, xsvc 98 }, 99 { "pseudo", "ddi_pseudo", NULL, 100 TYPE_EXACT, ILEVEL_0, srn 101 }, 102 { "memory-controller", "ddi_mem_ctrl", NULL, 103 TYPE_EXACT, ILEVEL_0, mc_node 104 }, 105 { "pseudo", "ddi_pseudo", "ucode", 106 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ucode, 107 }, 108 }; 109 110 DEVFSADM_CREATE_INIT_V0(misc_cbt); 111 112 static char *debug_mid = "misc_mid"; 113 114 typedef enum { 115 DRIVER_AGPPSEUDO = 0, 116 DRIVER_AGPTARGET, 117 DRIVER_CPUGART, 118 DRIVER_AGPMASTER_DRM_I915, 119 DRIVER_AGPMASTER_DRM_RADEON, 120 DRIVER_AGPMASTER_VGATEXT, 121 DRIVER_UNKNOWN 122 } driver_defs_t; 123 124 typedef struct { 125 char *driver_name; 126 int index; 127 } driver_name_table_entry_t; 128 129 static driver_name_table_entry_t driver_name_table[] = { 130 { "agpgart", DRIVER_AGPPSEUDO }, 131 { "agptarget", DRIVER_AGPTARGET }, 132 { "amd64_gart", DRIVER_CPUGART }, 133 /* AGP master device managed by drm driver */ 134 { "i915", DRIVER_AGPMASTER_DRM_I915 }, 135 { "radeon", DRIVER_AGPMASTER_DRM_RADEON }, 136 { "vgatext", DRIVER_AGPMASTER_VGATEXT }, 137 { NULL, DRIVER_UNKNOWN } 138 }; 139 140 static devfsadm_enumerate_t agptarget_rules[1] = 141 { "^agp$/^agptarget([0-9]+)$", 1, MATCH_ALL }; 142 static devfsadm_enumerate_t cpugart_rules[1] = 143 { "^agp$/^cpugart([0-9]+)$", 1, MATCH_ALL }; 144 static devfsadm_enumerate_t agpmaster_rules[1] = 145 { "^agp$/^agpmaster([0-9]+)$", 1, MATCH_ALL }; 146 147 static devfsadm_remove_t misc_remove_cbt[] = { 148 { "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS, 149 ILEVEL_0, devfsadm_rm_all 150 }, 151 { "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT, 152 ILEVEL_0, devfsadm_rm_all 153 }, 154 { "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE, 155 ILEVEL_0, devfsadm_rm_all 156 }, 157 { "disk", "^(diskette|rdiskette)([0-9]*)$", 158 RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all 159 }, 160 { "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE, 161 ILEVEL_1, devfsadm_rm_all 162 }, 163 { "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE, 164 ILEVEL_1, devfsadm_rm_all 165 }, 166 { "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE, 167 ILEVEL_1, devfsadm_rm_all 168 } 169 }; 170 171 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt); 172 173 /* 174 * Handles minor node type "ddi_display", in addition to generic processing 175 * done by display(). 176 * 177 * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility. 178 */ 179 /* ARGSUSED */ 180 int 181 vt00(di_minor_t minor, di_node_t node) 182 { 183 (void) devfsadm_secondary_link("vt00", "fb", 0); 184 return (DEVFSADM_CONTINUE); 185 } 186 187 /* 188 * type=ddi_block:diskette;addr=0,0;minor=c diskette 189 * type=ddi_block:diskette;addr=0,0;minor=c,raw rdiskette 190 * type=ddi_block:diskette;addr1=0;minor=c diskette\A2 191 * type=ddi_block:diskette;addr1=0;minor=c,raw rdiskette\A2 192 */ 193 static int 194 diskette(di_minor_t minor, di_node_t node) 195 { 196 int flags = 0; 197 char *a2; 198 char link[PATH_MAX]; 199 char *addr = di_bus_addr(node); 200 char *mn = di_minor_name(minor); 201 202 if (system_labeled) 203 flags = DA_ADD|DA_FLOPPY; 204 205 if (strcmp(addr, "0,0") == 0) { 206 if (strcmp(mn, "c") == 0) { 207 (void) devfsadm_mklink("diskette", node, minor, flags); 208 } else if (strcmp(mn, "c,raw") == 0) { 209 (void) devfsadm_mklink("rdiskette", node, minor, flags); 210 } 211 212 } 213 214 if (addr[0] == '0') { 215 if ((a2 = strchr(addr, ',')) != NULL) { 216 a2++; 217 if (strcmp(mn, "c") == 0) { 218 (void) strcpy(link, "diskette"); 219 (void) strcat(link, a2); 220 (void) devfsadm_mklink(link, node, minor, 221 flags); 222 } else if (strcmp(mn, "c,raw") == 0) { 223 (void) strcpy(link, "rdiskette"); 224 (void) strcat(link, a2); 225 (void) devfsadm_mklink(link, node, minor, 226 flags); 227 } 228 } 229 } 230 231 return (DEVFSADM_CONTINUE); 232 } 233 234 /* 235 * type=ddi_printer;name=lp;addr=1,3bc lp0 236 * type=ddi_printer;name=lp;addr=1,378 lp1 237 * type=ddi_printer;name=lp;addr=1,278 lp2 238 */ 239 static int 240 lp(di_minor_t minor, di_node_t node) 241 { 242 char *addr = di_bus_addr(node); 243 char *buf; 244 char path[PATH_MAX + 1]; 245 devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL}; 246 247 if (strcmp(addr, "1,3bc") == 0) { 248 (void) devfsadm_mklink("lp0", node, minor, 0); 249 250 } else if (strcmp(addr, "1,378") == 0) { 251 (void) devfsadm_mklink("lp1", node, minor, 0); 252 253 } else if (strcmp(addr, "1,278") == 0) { 254 (void) devfsadm_mklink("lp2", node, minor, 0); 255 } 256 257 if (strcmp(di_driver_name(node), "ecpp") != 0) { 258 return (DEVFSADM_CONTINUE); 259 } 260 261 if ((buf = di_devfs_path(node)) == NULL) { 262 return (DEVFSADM_CONTINUE); 263 } 264 265 (void) snprintf(path, sizeof (path), "%s:%s", 266 buf, di_minor_name(minor)); 267 268 di_devfs_path_free(buf); 269 270 if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) { 271 return (DEVFSADM_CONTINUE); 272 } 273 274 (void) snprintf(path, sizeof (path), "ecpp%s", buf); 275 free(buf); 276 (void) devfsadm_mklink(path, node, minor, 0); 277 return (DEVFSADM_CONTINUE); 278 } 279 280 /* 281 * type=ddi_serial:mb;minor=a tty00 282 * type=ddi_serial:mb;minor=b tty01 283 * type=ddi_serial:mb;minor=c tty02 284 * type=ddi_serial:mb;minor=d tty03 285 */ 286 static int 287 serial(di_minor_t minor, di_node_t node) 288 { 289 290 char *mn = di_minor_name(minor); 291 char link[PATH_MAX]; 292 293 (void) strcpy(link, "tty"); 294 (void) strcat(link, mn); 295 (void) devfsadm_mklink(link, node, minor, 0); 296 297 if (strcmp(mn, "a") == 0) { 298 (void) devfsadm_mklink("tty00", node, minor, 0); 299 300 } else if (strcmp(mn, "b") == 0) { 301 (void) devfsadm_mklink("tty01", node, minor, 0); 302 303 } else if (strcmp(mn, "c") == 0) { 304 (void) devfsadm_mklink("tty02", node, minor, 0); 305 306 } else if (strcmp(mn, "d") == 0) { 307 (void) devfsadm_mklink("tty03", node, minor, 0); 308 } 309 return (DEVFSADM_CONTINUE); 310 } 311 312 /* 313 * type=ddi_serial:dialout,mb;minor=a,cu ttyd0 314 * type=ddi_serial:dialout,mb;minor=b,cu ttyd1 315 * type=ddi_serial:dialout,mb;minor=c,cu ttyd2 316 * type=ddi_serial:dialout,mb;minor=d,cu ttyd3 317 */ 318 static int 319 serial_dialout(di_minor_t minor, di_node_t node) 320 { 321 char *mn = di_minor_name(minor); 322 323 if (strcmp(mn, "a,cu") == 0) { 324 (void) devfsadm_mklink("ttyd0", node, minor, 0); 325 (void) devfsadm_mklink("cua0", node, minor, 0); 326 327 } else if (strcmp(mn, "b,cu") == 0) { 328 (void) devfsadm_mklink("ttyd1", node, minor, 0); 329 (void) devfsadm_mklink("cua1", node, minor, 0); 330 331 } else if (strcmp(mn, "c,cu") == 0) { 332 (void) devfsadm_mklink("ttyd2", node, minor, 0); 333 (void) devfsadm_mklink("cua2", node, minor, 0); 334 335 } else if (strcmp(mn, "d,cu") == 0) { 336 (void) devfsadm_mklink("ttyd3", node, minor, 0); 337 (void) devfsadm_mklink("cua3", node, minor, 0); 338 } 339 return (DEVFSADM_CONTINUE); 340 } 341 342 static int 343 kdmouse(di_minor_t minor, di_node_t node) 344 { 345 (void) devfsadm_mklink("kdmouse", node, minor, 0); 346 return (DEVFSADM_CONTINUE); 347 } 348 349 static int 350 ipmi(di_minor_t minor, di_node_t node) 351 { 352 /* 353 * Follow convention from other systems, and include an instance#, 354 * even though there will only be one. 355 */ 356 (void) devfsadm_mklink("ipmi0", node, minor, 0); 357 return (DEVFSADM_CONTINUE); 358 } 359 360 static int 361 smbios(di_minor_t minor, di_node_t node) 362 { 363 (void) devfsadm_mklink("smbios", node, minor, 0); 364 return (DEVFSADM_CONTINUE); 365 } 366 367 static int 368 agp_process(di_minor_t minor, di_node_t node) 369 { 370 char *minor_nm, *drv_nm; 371 char *devfspath; 372 char *I_path, *p_path, *buf; 373 char *name = (char *)NULL; 374 int i, index; 375 devfsadm_enumerate_t rules[1]; 376 377 minor_nm = di_minor_name(minor); 378 drv_nm = di_driver_name(node); 379 380 if ((minor_nm == NULL) || (drv_nm == NULL)) { 381 return (DEVFSADM_CONTINUE); 382 } 383 384 devfsadm_print(debug_mid, "agp_process: minor=%s node=%s\n", 385 minor_nm, di_node_name(node)); 386 387 devfspath = di_devfs_path(node); 388 if (devfspath == NULL) { 389 devfsadm_print(debug_mid, "agp_process: devfspath is NULL\n"); 390 return (DEVFSADM_CONTINUE); 391 } 392 393 I_path = (char *)malloc(PATH_MAX); 394 395 if (I_path == NULL) { 396 di_devfs_path_free(devfspath); 397 devfsadm_print(debug_mid, "agp_process: malloc failed\n"); 398 return (DEVFSADM_CONTINUE); 399 } 400 401 p_path = (char *)malloc(PATH_MAX); 402 403 if (p_path == NULL) { 404 devfsadm_print(debug_mid, "agp_process: malloc failed\n"); 405 di_devfs_path_free(devfspath); 406 free(I_path); 407 return (DEVFSADM_CONTINUE); 408 } 409 410 (void) strlcpy(p_path, devfspath, PATH_MAX); 411 (void) strlcat(p_path, ":", PATH_MAX); 412 (void) strlcat(p_path, minor_nm, PATH_MAX); 413 di_devfs_path_free(devfspath); 414 415 devfsadm_print(debug_mid, "agp_process: path %s\n", p_path); 416 417 for (i = 0; ; i++) { 418 if ((driver_name_table[i].driver_name == NULL) || 419 (strcmp(drv_nm, driver_name_table[i].driver_name) == 0)) { 420 index = driver_name_table[i].index; 421 break; 422 } 423 } 424 switch (index) { 425 case DRIVER_AGPPSEUDO: 426 devfsadm_print(debug_mid, 427 "agp_process: psdeudo driver name\n"); 428 name = "agpgart"; 429 (void) snprintf(I_path, PATH_MAX, "%s", name); 430 devfsadm_print(debug_mid, 431 "mklink %s -> %s\n", I_path, p_path); 432 433 (void) devfsadm_mklink(I_path, node, minor, 0); 434 435 free(I_path); 436 free(p_path); 437 return (DEVFSADM_CONTINUE); 438 case DRIVER_AGPTARGET: 439 devfsadm_print(debug_mid, 440 "agp_process: target driver name\n"); 441 rules[0] = agptarget_rules[0]; 442 name = "agptarget"; 443 break; 444 case DRIVER_CPUGART: 445 devfsadm_print(debug_mid, 446 "agp_process: cpugart driver name\n"); 447 rules[0] = cpugart_rules[0]; 448 name = "cpugart"; 449 break; 450 case DRIVER_AGPMASTER_DRM_I915: 451 case DRIVER_AGPMASTER_DRM_RADEON: 452 case DRIVER_AGPMASTER_VGATEXT: 453 devfsadm_print(debug_mid, 454 "agp_process: agpmaster driver name\n"); 455 rules[0] = agpmaster_rules[0]; 456 name = "agpmaster"; 457 break; 458 case DRIVER_UNKNOWN: 459 devfsadm_print(debug_mid, 460 "agp_process: unknown driver name=%s\n", drv_nm); 461 free(I_path); 462 free(p_path); 463 return (DEVFSADM_CONTINUE); 464 } 465 466 if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) { 467 devfsadm_print(debug_mid, "agp_process: exit/coninue\n"); 468 free(I_path); 469 free(p_path); 470 return (DEVFSADM_CONTINUE); 471 } 472 473 474 (void) snprintf(I_path, PATH_MAX, "agp/%s%s", name, buf); 475 476 devfsadm_print(debug_mid, "agp_process: p_path=%s buf=%s\n", 477 p_path, buf); 478 479 free(buf); 480 481 devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path); 482 483 (void) devfsadm_mklink(I_path, node, minor, 0); 484 485 free(p_path); 486 free(I_path); 487 488 return (DEVFSADM_CONTINUE); 489 } 490 491 static int 492 drm_node(di_minor_t minor, di_node_t node) 493 { 494 char *minor_nm, *drv_nm; 495 char *devfspath; 496 char *I_path, *p_path, *buf; 497 char *name = "card"; 498 499 devfsadm_enumerate_t drm_rules[1] = {"^dri$/^card([0-9]+)$", 1, 500 MATCH_ALL }; 501 502 503 minor_nm = di_minor_name(minor); 504 drv_nm = di_driver_name(node); 505 if ((minor_nm == NULL) || (drv_nm == NULL)) { 506 return (DEVFSADM_CONTINUE); 507 } 508 509 devfsadm_print(debug_mid, "drm_node: minor=%s node=%s type=%s\n", 510 minor_nm, di_node_name(node), di_minor_nodetype(minor)); 511 512 devfspath = di_devfs_path(node); 513 if (devfspath == NULL) { 514 devfsadm_print(debug_mid, "drm_node: devfspath is NULL\n"); 515 return (DEVFSADM_CONTINUE); 516 } 517 518 I_path = (char *)malloc(PATH_MAX); 519 520 if (I_path == NULL) { 521 di_devfs_path_free(devfspath); 522 devfsadm_print(debug_mid, "drm_node: malloc failed\n"); 523 return (DEVFSADM_CONTINUE); 524 } 525 526 p_path = (char *)malloc(PATH_MAX); 527 528 if (p_path == NULL) { 529 devfsadm_print(debug_mid, "drm_node: malloc failed\n"); 530 di_devfs_path_free(devfspath); 531 free(I_path); 532 return (DEVFSADM_CONTINUE); 533 } 534 535 (void) strlcpy(p_path, devfspath, PATH_MAX); 536 (void) strlcat(p_path, ":", PATH_MAX); 537 (void) strlcat(p_path, minor_nm, PATH_MAX); 538 di_devfs_path_free(devfspath); 539 540 devfsadm_print(debug_mid, "drm_node: p_path %s\n", p_path); 541 542 if (devfsadm_enumerate_int(p_path, 0, &buf, drm_rules, 1)) { 543 free(p_path); 544 devfsadm_print(debug_mid, "drm_node: exit/coninue\n"); 545 return (DEVFSADM_CONTINUE); 546 } 547 (void) snprintf(I_path, PATH_MAX, "dri/%s%s", name, buf); 548 549 devfsadm_print(debug_mid, "drm_node: p_path=%s buf=%s\n", 550 p_path, buf); 551 552 free(buf); 553 554 devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path); 555 (void) devfsadm_mklink(I_path, node, minor, 0); 556 557 free(p_path); 558 free(I_path); 559 560 return (0); 561 } 562 563 /* 564 * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd 565 */ 566 static int 567 mc_node(di_minor_t minor, di_node_t node) 568 { 569 const char *minorname = di_minor_name(minor); 570 const char *busaddr = di_bus_addr(node); 571 char linkpath[PATH_MAX]; 572 int unitaddr; 573 char *c; 574 575 if (minorname == NULL || busaddr == NULL) 576 return (DEVFSADM_CONTINUE); 577 578 errno = 0; 579 unitaddr = strtol(busaddr, &c, 16); 580 581 if (errno != 0) 582 return (DEVFSADM_CONTINUE); 583 584 if (unitaddr == 0) { 585 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc"); 586 } else if (unitaddr >= MC_AMD_DEV_OFFSET) { 587 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 588 unitaddr - MC_AMD_DEV_OFFSET); 589 } else { 590 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 591 minor->dev_minor); 592 } 593 (void) devfsadm_mklink(linkpath, node, minor, 0); 594 return (DEVFSADM_CONTINUE); 595 } 596 597 /* 598 * Creates \M0 devlink for xsvc node 599 */ 600 static int 601 xsvc(di_minor_t minor, di_node_t node) 602 { 603 char *mn; 604 605 if (strcmp(di_node_name(node), "xsvc") != 0) 606 return (DEVFSADM_CONTINUE); 607 608 mn = di_minor_name(minor); 609 if (mn == NULL) 610 return (DEVFSADM_CONTINUE); 611 612 (void) devfsadm_mklink(mn, node, minor, 0); 613 return (DEVFSADM_CONTINUE); 614 } 615 616 /* 617 * Creates \M0 devlink for srn device 618 */ 619 static int 620 srn(di_minor_t minor, di_node_t node) 621 { 622 char *mn; 623 624 if (strcmp(di_node_name(node), "srn") != 0) 625 return (DEVFSADM_CONTINUE); 626 627 mn = di_minor_name(minor); 628 if (mn == NULL) 629 return (DEVFSADM_CONTINUE); 630 631 (void) devfsadm_mklink(mn, node, minor, 0); 632 return (DEVFSADM_CONTINUE); 633 } 634 635 /* 636 * /dev/ucode -> /devices/pseudo/ucode@0:ucode 637 */ 638 static int 639 ucode(di_minor_t minor, di_node_t node) 640 { 641 (void) devfsadm_mklink("ucode", node, minor, 0); 642 return (DEVFSADM_CONTINUE); 643 } 644