/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2018 Joyent, Inc. All rights reserved. * Copyright 2022 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include extern int system_labeled; static int ln_minor_name(di_minor_t minor, di_node_t node); static int lp(di_minor_t minor, di_node_t node); static int serial_dialout(di_minor_t minor, di_node_t node); static int serial(di_minor_t minor, di_node_t node); static int diskette(di_minor_t minor, di_node_t node); static int vt00(di_minor_t minor, di_node_t node); static int kdmouse(di_minor_t minor, di_node_t node); static int ipmi(di_minor_t minor, di_node_t node); static int mc_node(di_minor_t minor, di_node_t node); static int vmmctl(di_minor_t minor, di_node_t node); static int ppt(di_minor_t minor, di_node_t node); static devfsadm_create_t misc_cbt[] = { { "vt00", "ddi_display", NULL, TYPE_EXACT, ILEVEL_0, vt00 }, { "mouse", "ddi_mouse", "mouse8042", TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse }, { "pseudo", "ddi_pseudo", "ipmi", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi, }, { "pseudo", "ddi_pseudo", "smbios", TYPE_EXACT | DRV_EXACT, ILEVEL_1, ln_minor_name, }, /* floppies share the same class, but not link regex, as hard disks */ { "disk", "ddi_block:diskette", NULL, TYPE_EXACT, ILEVEL_1, diskette }, { "parallel", "ddi_printer", NULL, TYPE_EXACT, ILEVEL_1, lp }, { "serial", "ddi_serial:mb", NULL, TYPE_EXACT, ILEVEL_1, serial }, { "serial", "ddi_serial:dialout,mb", NULL, TYPE_EXACT, ILEVEL_1, serial_dialout }, { "pseudo", "ddi_pseudo", "xsvc", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, }, { "pseudo", "ddi_pseudo", "srn", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, }, { "memory-controller", "ddi_mem_ctrl", NULL, TYPE_EXACT, ILEVEL_0, mc_node }, { "pseudo", "ddi_pseudo", "ucode", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, }, { "pseudo", "ddi_pseudo", "viona", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, }, { "pseudo", "ddi_pseudo", "vmm", TYPE_EXACT | DRV_EXACT, ILEVEL_0, vmmctl, }, { "pseudo", "ddi_pseudo", "ppt", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ppt, }, { "pseudo", "ddi_pseudo", "vmm_drv_test", TYPE_EXACT | DRV_EXACT, ILEVEL_0, ln_minor_name, } }; DEVFSADM_CREATE_INIT_V0(misc_cbt); static devfsadm_remove_t misc_remove_cbt[] = { { "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all }, { "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all }, { "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE, ILEVEL_0, devfsadm_rm_all }, { "disk", "^(diskette|rdiskette)([0-9]*)$", RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all }, { "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all }, { "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all }, { "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all }, { "pseudo", "^viona$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all }, { "pseudo", "^vmmctl$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all }, { "pseudo", "^ppt$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all } }; DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt); /* * Any /dev/foo entry named after the minor name such as * /devices/.../driver@0:foo */ static int ln_minor_name(di_minor_t minor, di_node_t node) { (void) devfsadm_mklink(di_minor_name(minor), node, minor, 0); return (DEVFSADM_CONTINUE); } /* * Handles minor node type "ddi_display", in addition to generic processing * done by display(). * * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility. */ /* ARGSUSED */ int vt00(di_minor_t minor, di_node_t node) { (void) devfsadm_secondary_link("vt00", "fb", 0); return (DEVFSADM_CONTINUE); } /* * type=ddi_block:diskette;addr=0,0;minor=c diskette * type=ddi_block:diskette;addr=0,0;minor=c,raw rdiskette * type=ddi_block:diskette;addr1=0;minor=c diskette\A2 * type=ddi_block:diskette;addr1=0;minor=c,raw rdiskette\A2 */ static int diskette(di_minor_t minor, di_node_t node) { int flags = 0; char *a2; char link[PATH_MAX]; char *addr = di_bus_addr(node); char *mn = di_minor_name(minor); if (system_labeled) flags = DA_ADD|DA_FLOPPY; if (strcmp(addr, "0,0") == 0) { if (strcmp(mn, "c") == 0) { (void) devfsadm_mklink("diskette", node, minor, flags); } else if (strcmp(mn, "c,raw") == 0) { (void) devfsadm_mklink("rdiskette", node, minor, flags); } } if (addr[0] == '0') { if ((a2 = strchr(addr, ',')) != NULL) { a2++; if (strcmp(mn, "c") == 0) { (void) strcpy(link, "diskette"); (void) strcat(link, a2); (void) devfsadm_mklink(link, node, minor, flags); } else if (strcmp(mn, "c,raw") == 0) { (void) strcpy(link, "rdiskette"); (void) strcat(link, a2); (void) devfsadm_mklink(link, node, minor, flags); } } } return (DEVFSADM_CONTINUE); } /* * type=ddi_printer;name=lp;addr=1,3bc lp0 * type=ddi_printer;name=lp;addr=1,378 lp1 * type=ddi_printer;name=lp;addr=1,278 lp2 */ static int lp(di_minor_t minor, di_node_t node) { char *addr = di_bus_addr(node); char *buf; char path[PATH_MAX + 1]; devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL}; if (strcmp(addr, "1,3bc") == 0) { (void) devfsadm_mklink("lp0", node, minor, 0); } else if (strcmp(addr, "1,378") == 0) { (void) devfsadm_mklink("lp1", node, minor, 0); } else if (strcmp(addr, "1,278") == 0) { (void) devfsadm_mklink("lp2", node, minor, 0); } if (strcmp(di_driver_name(node), "ecpp") != 0) { return (DEVFSADM_CONTINUE); } if ((buf = di_devfs_path(node)) == NULL) { return (DEVFSADM_CONTINUE); } (void) snprintf(path, sizeof (path), "%s:%s", buf, di_minor_name(minor)); di_devfs_path_free(buf); if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) { return (DEVFSADM_CONTINUE); } (void) snprintf(path, sizeof (path), "ecpp%s", buf); free(buf); (void) devfsadm_mklink(path, node, minor, 0); return (DEVFSADM_CONTINUE); } /* * type=ddi_serial:mb;minor=a tty00 * type=ddi_serial:mb;minor=b tty01 * type=ddi_serial:mb;minor=c tty02 * type=ddi_serial:mb;minor=d tty03 */ static int serial(di_minor_t minor, di_node_t node) { char *mn = di_minor_name(minor); char link[PATH_MAX]; (void) strcpy(link, "tty"); (void) strcat(link, mn); (void) devfsadm_mklink(link, node, minor, 0); if (strcmp(mn, "a") == 0) { (void) devfsadm_mklink("tty00", node, minor, 0); } else if (strcmp(mn, "b") == 0) { (void) devfsadm_mklink("tty01", node, minor, 0); } else if (strcmp(mn, "c") == 0) { (void) devfsadm_mklink("tty02", node, minor, 0); } else if (strcmp(mn, "d") == 0) { (void) devfsadm_mklink("tty03", node, minor, 0); } return (DEVFSADM_CONTINUE); } /* * type=ddi_serial:dialout,mb;minor=a,cu ttyd0 * type=ddi_serial:dialout,mb;minor=b,cu ttyd1 * type=ddi_serial:dialout,mb;minor=c,cu ttyd2 * type=ddi_serial:dialout,mb;minor=d,cu ttyd3 */ static int serial_dialout(di_minor_t minor, di_node_t node) { char *mn = di_minor_name(minor); if (strcmp(mn, "a,cu") == 0) { (void) devfsadm_mklink("ttyd0", node, minor, 0); (void) devfsadm_mklink("cua0", node, minor, 0); } else if (strcmp(mn, "b,cu") == 0) { (void) devfsadm_mklink("ttyd1", node, minor, 0); (void) devfsadm_mklink("cua1", node, minor, 0); } else if (strcmp(mn, "c,cu") == 0) { (void) devfsadm_mklink("ttyd2", node, minor, 0); (void) devfsadm_mklink("cua2", node, minor, 0); } else if (strcmp(mn, "d,cu") == 0) { (void) devfsadm_mklink("ttyd3", node, minor, 0); (void) devfsadm_mklink("cua3", node, minor, 0); } return (DEVFSADM_CONTINUE); } static int kdmouse(di_minor_t minor, di_node_t node) { (void) devfsadm_mklink("kdmouse", node, minor, 0); return (DEVFSADM_CONTINUE); } static int ipmi(di_minor_t minor, di_node_t node) { /* * Follow convention from other systems, and include an instance#, * even though there will only be one. */ (void) devfsadm_mklink("ipmi0", node, minor, 0); return (DEVFSADM_CONTINUE); } /* * /dev/mc/mc -> /devices/.../pci1022,1102@,2:mc-amd */ static int mc_node(di_minor_t minor, di_node_t node) { const char *minorname = di_minor_name(minor); const char *busaddr = di_bus_addr(node); char linkpath[PATH_MAX]; int unitaddr; char *c; if (minorname == NULL || busaddr == NULL) return (DEVFSADM_CONTINUE); errno = 0; unitaddr = strtol(busaddr, &c, 16); if (errno != 0) return (DEVFSADM_CONTINUE); if (unitaddr == 0) { (void) snprintf(linkpath, sizeof (linkpath), "mc/mc"); } else if (unitaddr >= MC_AMD_DEV_OFFSET) { (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", unitaddr - MC_AMD_DEV_OFFSET); } else { (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", minor->dev_minor); } (void) devfsadm_mklink(linkpath, node, minor, 0); return (DEVFSADM_CONTINUE); } /* * /dev/vmmctl -> /devices/pseudo/vmm@0:ctl */ static int vmmctl(di_minor_t minor, di_node_t node) { if (strcmp(di_minor_name(minor), "ctl") == 0) (void) devfsadm_mklink("vmmctl", node, minor, 0); return (DEVFSADM_CONTINUE); } static int ppt(di_minor_t minor, di_node_t node) { char linkpath[PATH_MAX]; (void) snprintf(linkpath, sizeof (linkpath), "ppt%d", di_instance(node)); (void) devfsadm_mklink(linkpath, node, minor, 0); return (DEVFSADM_CONTINUE); }