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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2018 Joyent, Inc. All rights reserved. 25 */ 26 27 #include <regex.h> 28 #include <devfsadm.h> 29 #include <stdio.h> 30 #include <strings.h> 31 #include <stdlib.h> 32 #include <limits.h> 33 #include <ctype.h> 34 #include <sys/mc_amd.h> 35 #include <bsm/devalloc.h> 36 37 extern int system_labeled; 38 39 static int ln_minor_name(di_minor_t minor, di_node_t node); 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 ipmi(di_minor_t minor, di_node_t node); 47 static int mc_node(di_minor_t minor, di_node_t node); 48 49 static devfsadm_create_t misc_cbt[] = { 50 { "vt00", "ddi_display", NULL, 51 TYPE_EXACT, ILEVEL_0, vt00 52 }, 53 { "mouse", "ddi_mouse", "mouse8042", 54 TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse 55 }, 56 { "pseudo", "ddi_pseudo", "ipmi", 57 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi, 58 }, 59 { "pseudo", "ddi_pseudo", "smbios", 60 TYPE_EXACT | DRV_EXACT, ILEVEL_1, ln_minor_name, 61 }, 62 /* floppies share the same class, but not link regex, as hard disks */ 63 { "disk", "ddi_block:diskette", NULL, 64 TYPE_EXACT, ILEVEL_1, diskette 65 }, 66 { "parallel", "ddi_printer", NULL, 67 TYPE_EXACT, ILEVEL_1, lp 68 }, 69 { "serial", "ddi_serial:mb", NULL, 70 TYPE_EXACT, ILEVEL_1, serial 71 }, 72 { "serial", "ddi_serial:dialout,mb", NULL, 73 TYPE_EXACT, ILEVEL_1, serial_dialout 74 }, 75 { "pseudo", "ddi_pseudo", "xsvc", 76 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, 77 }, 78 { "pseudo", "ddi_pseudo", "srn", 79 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, 80 }, 81 { "memory-controller", "ddi_mem_ctrl", NULL, 82 TYPE_EXACT, ILEVEL_0, mc_node 83 }, 84 { "pseudo", "ddi_pseudo", "ucode", 85 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, 86 }, 87 }; 88 89 DEVFSADM_CREATE_INIT_V0(misc_cbt); 90 91 static devfsadm_remove_t misc_remove_cbt[] = { 92 { "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS, 93 ILEVEL_0, devfsadm_rm_all 94 }, 95 { "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT, 96 ILEVEL_0, devfsadm_rm_all 97 }, 98 { "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE, 99 ILEVEL_0, devfsadm_rm_all 100 }, 101 { "disk", "^(diskette|rdiskette)([0-9]*)$", 102 RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all 103 }, 104 { "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE, 105 ILEVEL_1, devfsadm_rm_all 106 }, 107 { "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE, 108 ILEVEL_1, devfsadm_rm_all 109 }, 110 { "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE, 111 ILEVEL_1, devfsadm_rm_all 112 } 113 }; 114 115 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt); 116 117 /* 118 * Any /dev/foo entry named after the minor name such as 119 * /devices/.../driver@0:foo 120 */ 121 static int 122 ln_minor_name(di_minor_t minor, di_node_t node) 123 { 124 (void) devfsadm_mklink(di_minor_name(minor), node, minor, 0); 125 return (DEVFSADM_CONTINUE); 126 } 127 128 /* 129 * Handles minor node type "ddi_display", in addition to generic processing 130 * done by display(). 131 * 132 * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility. 133 */ 134 /* ARGSUSED */ 135 int 136 vt00(di_minor_t minor, di_node_t node) 137 { 138 (void) devfsadm_secondary_link("vt00", "fb", 0); 139 return (DEVFSADM_CONTINUE); 140 } 141 142 /* 143 * type=ddi_block:diskette;addr=0,0;minor=c diskette 144 * type=ddi_block:diskette;addr=0,0;minor=c,raw rdiskette 145 * type=ddi_block:diskette;addr1=0;minor=c diskette\A2 146 * type=ddi_block:diskette;addr1=0;minor=c,raw rdiskette\A2 147 */ 148 static int 149 diskette(di_minor_t minor, di_node_t node) 150 { 151 int flags = 0; 152 char *a2; 153 char link[PATH_MAX]; 154 char *addr = di_bus_addr(node); 155 char *mn = di_minor_name(minor); 156 157 if (system_labeled) 158 flags = DA_ADD|DA_FLOPPY; 159 160 if (strcmp(addr, "0,0") == 0) { 161 if (strcmp(mn, "c") == 0) { 162 (void) devfsadm_mklink("diskette", node, minor, flags); 163 } else if (strcmp(mn, "c,raw") == 0) { 164 (void) devfsadm_mklink("rdiskette", node, minor, flags); 165 } 166 167 } 168 169 if (addr[0] == '0') { 170 if ((a2 = strchr(addr, ',')) != NULL) { 171 a2++; 172 if (strcmp(mn, "c") == 0) { 173 (void) strcpy(link, "diskette"); 174 (void) strcat(link, a2); 175 (void) devfsadm_mklink(link, node, minor, 176 flags); 177 } else if (strcmp(mn, "c,raw") == 0) { 178 (void) strcpy(link, "rdiskette"); 179 (void) strcat(link, a2); 180 (void) devfsadm_mklink(link, node, minor, 181 flags); 182 } 183 } 184 } 185 186 return (DEVFSADM_CONTINUE); 187 } 188 189 /* 190 * type=ddi_printer;name=lp;addr=1,3bc lp0 191 * type=ddi_printer;name=lp;addr=1,378 lp1 192 * type=ddi_printer;name=lp;addr=1,278 lp2 193 */ 194 static int 195 lp(di_minor_t minor, di_node_t node) 196 { 197 char *addr = di_bus_addr(node); 198 char *buf; 199 char path[PATH_MAX + 1]; 200 devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL}; 201 202 if (strcmp(addr, "1,3bc") == 0) { 203 (void) devfsadm_mklink("lp0", node, minor, 0); 204 205 } else if (strcmp(addr, "1,378") == 0) { 206 (void) devfsadm_mklink("lp1", node, minor, 0); 207 208 } else if (strcmp(addr, "1,278") == 0) { 209 (void) devfsadm_mklink("lp2", node, minor, 0); 210 } 211 212 if (strcmp(di_driver_name(node), "ecpp") != 0) { 213 return (DEVFSADM_CONTINUE); 214 } 215 216 if ((buf = di_devfs_path(node)) == NULL) { 217 return (DEVFSADM_CONTINUE); 218 } 219 220 (void) snprintf(path, sizeof (path), "%s:%s", 221 buf, di_minor_name(minor)); 222 223 di_devfs_path_free(buf); 224 225 if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) { 226 return (DEVFSADM_CONTINUE); 227 } 228 229 (void) snprintf(path, sizeof (path), "ecpp%s", buf); 230 free(buf); 231 (void) devfsadm_mklink(path, node, minor, 0); 232 return (DEVFSADM_CONTINUE); 233 } 234 235 /* 236 * type=ddi_serial:mb;minor=a tty00 237 * type=ddi_serial:mb;minor=b tty01 238 * type=ddi_serial:mb;minor=c tty02 239 * type=ddi_serial:mb;minor=d tty03 240 */ 241 static int 242 serial(di_minor_t minor, di_node_t node) 243 { 244 245 char *mn = di_minor_name(minor); 246 char link[PATH_MAX]; 247 248 (void) strcpy(link, "tty"); 249 (void) strcat(link, mn); 250 (void) devfsadm_mklink(link, node, minor, 0); 251 252 if (strcmp(mn, "a") == 0) { 253 (void) devfsadm_mklink("tty00", node, minor, 0); 254 255 } else if (strcmp(mn, "b") == 0) { 256 (void) devfsadm_mklink("tty01", node, minor, 0); 257 258 } else if (strcmp(mn, "c") == 0) { 259 (void) devfsadm_mklink("tty02", node, minor, 0); 260 261 } else if (strcmp(mn, "d") == 0) { 262 (void) devfsadm_mklink("tty03", node, minor, 0); 263 } 264 return (DEVFSADM_CONTINUE); 265 } 266 267 /* 268 * type=ddi_serial:dialout,mb;minor=a,cu ttyd0 269 * type=ddi_serial:dialout,mb;minor=b,cu ttyd1 270 * type=ddi_serial:dialout,mb;minor=c,cu ttyd2 271 * type=ddi_serial:dialout,mb;minor=d,cu ttyd3 272 */ 273 static int 274 serial_dialout(di_minor_t minor, di_node_t node) 275 { 276 char *mn = di_minor_name(minor); 277 278 if (strcmp(mn, "a,cu") == 0) { 279 (void) devfsadm_mklink("ttyd0", node, minor, 0); 280 (void) devfsadm_mklink("cua0", node, minor, 0); 281 282 } else if (strcmp(mn, "b,cu") == 0) { 283 (void) devfsadm_mklink("ttyd1", node, minor, 0); 284 (void) devfsadm_mklink("cua1", node, minor, 0); 285 286 } else if (strcmp(mn, "c,cu") == 0) { 287 (void) devfsadm_mklink("ttyd2", node, minor, 0); 288 (void) devfsadm_mklink("cua2", node, minor, 0); 289 290 } else if (strcmp(mn, "d,cu") == 0) { 291 (void) devfsadm_mklink("ttyd3", node, minor, 0); 292 (void) devfsadm_mklink("cua3", node, minor, 0); 293 } 294 return (DEVFSADM_CONTINUE); 295 } 296 297 static int 298 kdmouse(di_minor_t minor, di_node_t node) 299 { 300 (void) devfsadm_mklink("kdmouse", node, minor, 0); 301 return (DEVFSADM_CONTINUE); 302 } 303 304 static int 305 ipmi(di_minor_t minor, di_node_t node) 306 { 307 /* 308 * Follow convention from other systems, and include an instance#, 309 * even though there will only be one. 310 */ 311 (void) devfsadm_mklink("ipmi0", node, minor, 0); 312 return (DEVFSADM_CONTINUE); 313 } 314 315 /* 316 * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd 317 */ 318 static int 319 mc_node(di_minor_t minor, di_node_t node) 320 { 321 const char *minorname = di_minor_name(minor); 322 const char *busaddr = di_bus_addr(node); 323 char linkpath[PATH_MAX]; 324 int unitaddr; 325 char *c; 326 327 if (minorname == NULL || busaddr == NULL) 328 return (DEVFSADM_CONTINUE); 329 330 errno = 0; 331 unitaddr = strtol(busaddr, &c, 16); 332 333 if (errno != 0) 334 return (DEVFSADM_CONTINUE); 335 336 if (unitaddr == 0) { 337 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc"); 338 } else if (unitaddr >= MC_AMD_DEV_OFFSET) { 339 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 340 unitaddr - MC_AMD_DEV_OFFSET); 341 } else { 342 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 343 minor->dev_minor); 344 } 345 (void) devfsadm_mklink(linkpath, node, minor, 0); 346 return (DEVFSADM_CONTINUE); 347 } 348