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