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