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