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