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