diff options
24 files changed, 1195 insertions, 21 deletions
@@ -4928,6 +4928,7 @@ f usr/lib/amd64/libpool.so.1 0755 root bin s usr/lib/amd64/libpool.so=libpool.so.1 s usr/lib/amd64/libposix4.so.1=../../../lib/amd64/librt.so.1 s usr/lib/amd64/libposix4.so=../../../lib/amd64/librt.so.1 +f usr/lib/amd64/libppt.so.1 0755 root bin s usr/lib/amd64/libproc.so.1=../../../lib/amd64/libproc.so.1 s usr/lib/amd64/libproc.so=../../../lib/amd64/libproc.so.1 f usr/lib/amd64/libproject.so.1 0755 root bin @@ -6379,6 +6380,7 @@ s usr/lib/libposix4.so.1=../../lib/librt.so.1 s usr/lib/libposix4.so=../../lib/librt.so.1 f usr/lib/libpp.so.1 0755 root bin s usr/lib/libpp.so=libpp.so.1 +f usr/lib/libppt.so.1 0755 root bin s usr/lib/libproc.so.1=../../lib/libproc.so.1 s usr/lib/libproc.so=../../lib/libproc.so.1 f usr/lib/libproject.so.1 0755 root bin @@ -10450,6 +10452,7 @@ f usr/sbin/poolbind 0555 root bin f usr/sbin/poolcfg 0555 root bin h usr/sbin/ports=usr/sbin/devfsadm h usr/sbin/poweroff=usr/sbin/halt +f usr/sbin/pptadm 0555 root bin f usr/sbin/praudit 0555 root bin f usr/sbin/projadd 0555 root sys f usr/sbin/projdel 0555 root sys @@ -12093,6 +12096,7 @@ f usr/share/man/man1m/poold.1m 0444 root bin f usr/share/man/man1m/poolstat.1m 0444 root bin f usr/share/man/man1m/ports.1m 0444 root bin s usr/share/man/man1m/poweroff.1m=halt.1m +f usr/share/man/man1m/pptadm.1m 0444 root bin f usr/share/man/man1m/praudit.1m 0444 root bin f usr/share/man/man1m/projadd.1m 0444 root bin f usr/share/man/man1m/projdel.1m 0444 root bin diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index b8c25c83da..b5f18f9ca1 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -24,6 +24,7 @@ # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright 2015 Garrett D'Amore <garrett@damore.org> # Copyright 2016 Nexenta Systems, Inc. +# Copyright 2018 Joyent, Inc. # # include global definitions @@ -235,6 +236,7 @@ COMMON_SUBDIRS = \ cmd/printf \ cmd/latencytop \ cmd/ppgsz \ + cmd/pptadm \ cmd/praudit \ cmd/prctl \ cmd/priocntl \ @@ -409,6 +411,7 @@ COMMON_SUBDIRS = \ lib/libpicltree \ lib/libpkg \ lib/libpool \ + lib/libppt \ lib/libproc \ lib/libpthread \ lib/libraidcfg \ diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 9d42f388cd..d86e9ae3a4 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -317,6 +317,7 @@ COMMON_SUBDIRS= \ ppgsz \ pg \ plockstat \ + pptadm \ pr \ prctl \ printf \ diff --git a/usr/src/cmd/pptadm/Makefile b/usr/src/cmd/pptadm/Makefile new file mode 100644 index 0000000000..3be558a7a0 --- /dev/null +++ b/usr/src/cmd/pptadm/Makefile @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2018 Joyent, Inc. +# + +PROG = pptadm +OBJS = pptadm.o +SRCS = $(OBJS:%.o=%.c) + +include ../Makefile.cmd +include ../Makefile.ctf + +LDLIBS += -lofmt -lppt -lnvpair + +CSTD = $(CSTD_GNU99) +C99LMODE = -Xc99=%all + +CLEANFILES += $(OBJS) + +.KEEP_STATE: + +all: $(OBJS) $(PROG) + +install: all $(ROOTUSRSBINPROG) + +clean: + -$(RM) $(CLEANFILES) + +lint: lint_SRCS + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +include ../Makefile.targ diff --git a/usr/src/cmd/pptadm/pptadm.c b/usr/src/cmd/pptadm/pptadm.c new file mode 100644 index 0000000000..c6b9094408 --- /dev/null +++ b/usr/src/cmd/pptadm/pptadm.c @@ -0,0 +1,205 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * Copyright 2018 Joyent, Inc. + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <getopt.h> +#include <string.h> +#include <ofmt.h> +#include <err.h> + +#include <libppt.h> + +typedef enum field { + PPT_DEV, + PPT_VENDOR, + PPT_DEVICE, + PPT_SUBVENDOR, + PPT_SUBDEVICE, + PPT_REV, + PPT_PATH, + PPT_LABEL +} field_t; + +const char *valname[] = { + "dev", + "vendor-id", + "device-id", + "subsystem-vendor-id", + "subsystem-id", + "revision-id", + "path", + "label" +}; + +static ofmt_cb_t print_field; + +static ofmt_field_t fields[] = { +/* name, field width, index, callback */ +{ "DEV", sizeof ("/dev/pptXX"), PPT_DEV, print_field }, +{ "VENDOR", sizeof ("VENDOR"), PPT_VENDOR, print_field }, +{ "DEVICE", sizeof ("DEVICE"), PPT_DEVICE, print_field }, +{ "SUBVENDOR", sizeof ("SUBVENDOR"), PPT_SUBVENDOR, print_field }, +{ "SUBDEVICE", sizeof ("SUBDEVICE"), PPT_SUBDEVICE, print_field }, +{ "REV", sizeof ("REV"), PPT_REV, print_field }, +{ "PATH", 50, PPT_PATH, print_field }, +{ "LABEL", 60, PPT_LABEL, print_field }, +{ NULL, 0, 0, NULL }, +}; + +static void +usage(const char *errmsg) +{ + if (errmsg != NULL) + (void) fprintf(stderr, "pptadm: %s\n", errmsg); + (void) fprintf(errmsg != NULL ? stderr : stdout, + "Usage:\n" + "pptadm list [ -j ]\n" + "pptadm list [-ap] [-o fields]\n"); + exit(errmsg != NULL ? EXIT_FAILURE : EXIT_SUCCESS); +} + +/* PRINTFLIKE1 */ +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrx(EXIT_FAILURE, fmt, ap); + va_end(ap); +} + +static boolean_t +print_field(ofmt_arg_t *arg, char *buf, uint_t bufsize) +{ + nvlist_t *nvl = arg->ofmt_cbarg; + nvpair_t *nvp = NULL; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + const char *name = nvpair_name(nvp); + char *val = NULL; + + (void) nvpair_value_string(nvp, &val); + + if (strcmp(name, valname[arg->ofmt_id]) != 0) + continue; + + (void) snprintf(buf, bufsize, "%s", val); + return (B_TRUE); + } + + (void) snprintf(buf, bufsize, "--"); + return (B_TRUE); +} + +static int +list(int argc, char *argv[]) +{ + const char *fields_str = NULL; + boolean_t parsable = B_FALSE; + boolean_t json = B_FALSE; + boolean_t all = B_FALSE; + uint_t ofmtflags = 0; + ofmt_status_t oferr; + ofmt_handle_t ofmt; + int opt; + + while ((opt = getopt(argc, argv, "ahjo:p")) != -1) { + switch (opt) { + case 'a': + all = B_TRUE; + break; + case 'h': + usage(NULL); + break; + case 'j': + json = B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + case 'p': + ofmtflags |= OFMT_PARSABLE; + parsable = B_TRUE; + break; + default: + usage("unrecognized option"); + break; + } + } + + if (optind == (argc - 1)) + usage("unused arguments"); + + if (json && (parsable || fields_str != NULL)) + usage("-j option cannot be used with -p or -o options"); + + if (fields_str == NULL) { + if (parsable) + usage("-o must be provided when using -p option"); + fields_str = "dev,vendor,device,path"; + } + + oferr = ofmt_open(fields_str, fields, ofmtflags, 0, &ofmt); + + ofmt_check(oferr, parsable, ofmt, die, warn); + + nvlist_t *nvl = all ? ppt_list() : ppt_list_assigned(); + nvpair_t *nvp = NULL; + + if (json) { + if (printf("{\n\t\"devices\": [\n") < 0) + err(EXIT_FAILURE, "failed to write JSON"); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + nvlist_t *props; + + (void) nvpair_value_nvlist(nvp, &props); + + if (json) { + if (printf("\t\t") < 0) + err(EXIT_FAILURE, "failed to write JSON"); + if (nvlist_print_json(stdout, props) < 0) + err(EXIT_FAILURE, "failed to write JSON"); + if (nvlist_next_nvpair(nvl, nvp) != NULL) + (void) printf(",\n"); + } else { + ofmt_print(ofmt, props); + } + } + + if (json) { + if (printf("\n\t]\n}\n") < 0) + err(EXIT_FAILURE, "failed to write JSON"); + } + + nvlist_free(nvl); + ofmt_close(ofmt); + return (EXIT_SUCCESS); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 1) + return (list(argc - 1, argv)); + + if (strcmp(argv[1], "list") == 0) { + return (list(argc - 1, &argv[1])); + } else { + usage("unknown sub-command"); + } + + return (EXIT_SUCCESS); +} diff --git a/usr/src/cmd/zoneadmd/Makefile.com b/usr/src/cmd/zoneadmd/Makefile.com index 7f78fb2e05..6312c00ad5 100644 --- a/usr/src/cmd/zoneadmd/Makefile.com +++ b/usr/src/cmd/zoneadmd/Makefile.com @@ -35,7 +35,7 @@ OBJS= zoneadmd.o zcons.o zfd.o vplat.o log.o CFLAGS += $(CCVERBOSE) LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ - -linetutil -lproc -lscf + -linetutil -lproc -lscf -lppt CSTD= $(CSTD_GNU99) diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index d67e8d824f..f466836b96 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017, Joyent Inc. + * Copyright 2018, Joyent Inc. * Copyright (c) 2015, 2016 by Delphix. All rights reserved. */ @@ -1198,9 +1198,15 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) goto cleanup; } while (zonecfg_getdevent(snap_hndl, &ztab) == Z_OK) { - if (di_prof_add_dev(prof, ztab.zone_dev_match)) { + char path[MAXPATHLEN]; + + if ((err = resolve_device_match(zlogp, &ztab, + path, sizeof (path))) != Z_OK) + goto cleanup; + + if (di_prof_add_dev(prof, path)) { zerror(zlogp, B_TRUE, "failed to add " - "user-specified device"); + "user-specified device '%s'", path); goto cleanup; } } diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c index 21c6460eeb..51954a9f3e 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.c +++ b/usr/src/cmd/zoneadmd/zoneadmd.c @@ -105,6 +105,7 @@ #include <sys/dls_mgmt.h> #include <libscf.h> #include <uuid/uuid.h> +#include <libppt.h> #include <libzonecfg.h> #include <zonestat_impl.h> @@ -830,6 +831,46 @@ set_zonecfg_env(char *rsrc, char *attr, char *name, char *val) } /* + * Resolve a device:match value to a path. This is only different for PPT + * devices, where we expect the match property to be a /devices/... path, and + * configured for PPT already. + */ +int +resolve_device_match(zlog_t *zlogp, struct zone_devtab *dtab, + char *path, size_t len) +{ + struct zone_res_attrtab *rap; + + for (rap = dtab->zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + if (strcmp(rap->zone_res_attr_name, "model") == 0 && + strcmp(rap->zone_res_attr_value, "passthru") == 0) + break; + } + + if (rap == NULL) { + if (strlcpy(path, dtab->zone_dev_match, len) >= len) + return (Z_INVAL); + return (Z_OK); + } + + if (strncmp(dtab->zone_dev_match, "/devices", + strlen("/devices")) != 0) { + zerror(zlogp, B_FALSE, "invalid passthru match value '%s'", + dtab->zone_dev_match); + return (Z_INVAL); + } + + if (ppt_devpath_to_dev(dtab->zone_dev_match, path, len) != 0) { + zerror(zlogp, B_TRUE, "failed to resolve passthru device %s", + dtab->zone_dev_match); + return (Z_INVAL); + } + + return (Z_OK); +} + +/* * Export various zonecfg properties into environment for the boot and state * change hooks. * @@ -846,7 +887,7 @@ set_zonecfg_env(char *rsrc, char *attr, char *name, char *val) * SmartOS. */ static int -setup_subproc_env(boolean_t debug) +setup_subproc_env(zlog_t *zlogp, boolean_t debug) { int res; struct zone_nwiftab ntab; @@ -931,17 +972,19 @@ setup_subproc_env(boolean_t debug) dev_resources[0] = '\0'; while (zonecfg_getdevent(snap_hndl, &dtab) == Z_OK) { + char *match = dtab.zone_dev_match; struct zone_res_attrtab *rap; - char *match; + char path[MAXPATHLEN]; - match = dtab.zone_dev_match; + res = resolve_device_match(zlogp, &dtab, path, sizeof (path)); + if (res != Z_OK) + goto done; /* - * In the environment variable name, the value of match will be - * mangled. Thus, we store the value of match in a "path" - * environment variable. + * Even if not modified, the match path will be mangled in the + * environment variable name, so we always store the value here. */ - set_zonecfg_env(RSRC_DEV, match, "path", match); + set_zonecfg_env(RSRC_DEV, match, "path", path); for (rap = dtab.zone_dev_attrp; rap != NULL; rap = rap->zone_res_attr_next) { @@ -1078,7 +1121,7 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr, boolean_t debug) } closefrom(STDERR_FILENO + 1); - if (setup_subproc_env(debug) != Z_OK) { + if (setup_subproc_env(zlogp, debug) != Z_OK) { (void) fprintf(stderr, "failed to setup environment"); _exit(127); } diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h index a170759e50..4953479e64 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.h +++ b/usr/src/cmd/zoneadmd/zoneadmd.h @@ -187,6 +187,12 @@ extern int init_template(void); */ extern int do_subproc(zlog_t *, char *, char **, boolean_t); +/* + * Resource handling. + */ +extern int resolve_device_match(zlog_t *, struct zone_devtab *, + char *, size_t); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 9efd87bc10..ee1855d850 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -183,6 +183,7 @@ SUBDIRS += \ libpkg \ libpool \ libpp \ + libppt \ libproc \ libproject \ libpthread \ @@ -448,6 +449,7 @@ HDRSUBDIRS= \ libpicltree \ libpool \ libpp \ + libppt \ libproc \ libraidcfg \ librcm \ @@ -655,6 +657,7 @@ libpctx: libproc libpkg: libscf libadm libpool: libscf libexacct libpp: libast +libppt: libpcidb libdevinfo libcmdutils libproc: ../cmd/sgs/librtld_db ../cmd/sgs/libelf libctf $(INTEL_BLD)libproc: libsaveargs libproject: libpool libproc libsecdb diff --git a/usr/src/lib/libppt/Makefile b/usr/src/lib/libppt/Makefile new file mode 100644 index 0000000000..21c26d447e --- /dev/null +++ b/usr/src/lib/libppt/Makefile @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) $(BUILD64) $(MACH64) + +HDRS = libppt.h +HDRDIR = common + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: $(ROOTHDRS) + +all install: install_h + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libppt/Makefile.com b/usr/src/lib/libppt/Makefile.com new file mode 100644 index 0000000000..7b2ff4885f --- /dev/null +++ b/usr/src/lib/libppt/Makefile.com @@ -0,0 +1,46 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +LIBRARY = libppt.a +VERS = .1 + +OBJECTS = libppt.o + +include $(SRC)/lib/Makefile.lib + +SRCDIR = ../common + +LIBS = $(DYNLIB) $(LINTLIB) +SRCS = $(SRCDIR)/libppt.c + +CSTD= $(CSTD_GNU99) +C99LMODE= -Xc99=%all + +# +# lint doesn't like %4s in sscanf(). +# +LINTFLAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2 +LINTFLAGS64 += -erroff=E_BAD_FORMAT_ARG_TYPE2 + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) +LDLIBS += -lpcidb -ldevinfo -lcmdutils -lnvpair -lc + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libppt/amd64/Makefile b/usr/src/lib/libppt/amd64/Makefile new file mode 100644 index 0000000000..5a304d7fe7 --- /dev/null +++ b/usr/src/lib/libppt/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libppt/common/libppt.c b/usr/src/lib/libppt/common/libppt.c new file mode 100644 index 0000000000..7e8385da06 --- /dev/null +++ b/usr/src/lib/libppt/common/libppt.c @@ -0,0 +1,506 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + * + * Convenience routines for identifying current or available devices that are + * suitable for PCI passthrough to a bhyve guest. + */ + +#include <libdevinfo.h> +#include <libppt.h> + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/list.h> +#include <strings.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pcidb.h> +#include <glob.h> + +typedef struct node_data { + pcidb_hdl_t *nd_db; + list_t nd_matches; + nvlist_t *nd_nvl; + int nd_err; +} node_data_t; + +typedef struct ppt_match { + list_node_t pm_list; + char pm_path[MAXPATHLEN]; + char pm_vendor[5]; + char pm_device[5]; +} ppt_match_t; + +static boolean_t +is_pci(di_node_t di_node) +{ + char *svals; + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(di_node), + "device_type", &svals) != 1) + return (B_FALSE); + + return (strcmp(svals, "pci") == 0 || strcmp(svals, "pciex") == 0); +} + +static int +populate_int_prop(di_node_t di_node, nvlist_t *nvl, const char *name, int *ival) +{ + char val[20]; + int *ivals; + int err; + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, di_node, name, &ivals) != 1) + return (errno); + + (void) snprintf(val, sizeof (val), "%x", ivals[0]); + + err = nvlist_add_string(nvl, name, val); + + if (err == 0 && ival != NULL) + *ival = ivals[0]; + + return (err); +} + +static int +dev_getlabel(pcidb_hdl_t *db, int vid, int did, char *buf, size_t buflen) +{ + pcidb_vendor_t *vend = NULL; + pcidb_device_t *dev = NULL; + + if ((vend = pcidb_lookup_vendor(db, vid)) == NULL) + return (ENOENT); + + if ((dev = pcidb_lookup_device_by_vendor(vend, did)) == NULL) + return (ENOENT); + + (void) snprintf(buf, buflen, "%s %s", pcidb_vendor_name(vend), + pcidb_device_name(dev)); + + return (0); +} + +static nvlist_t * +dev_getinfo(di_node_t di_node, pcidb_hdl_t *db, + const char *dev, const char *path) +{ + char label[MAXPATHLEN]; + nvlist_t *nvl = NULL; + int vid, did; + int err; + + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto out; + + if (dev != NULL && (err = nvlist_add_string(nvl, "dev", dev)) != 0) + goto out; + if ((err = nvlist_add_string(nvl, "path", path)) != 0) + goto out; + if ((err = populate_int_prop(di_node, nvl, "vendor-id", &vid)) != 0) + goto out; + if ((err = populate_int_prop(di_node, nvl, "device-id", &did)) != 0) + goto out; + if ((err = populate_int_prop(di_node, nvl, + "subsystem-vendor-id", NULL)) != 0) + goto out; + if ((err = populate_int_prop(di_node, nvl, "subsystem-id", NULL)) != 0) + goto out; + if ((err = populate_int_prop(di_node, nvl, "revision-id", NULL)) != 0) + goto out; + + err = dev_getlabel(db, vid, did, label, sizeof (label)); + + if (err == 0) { + err = nvlist_add_string(nvl, "label", label); + } else if (err == ENOENT) { + err = 0; + } + +out: + if (err) { + nvlist_free(nvl); + errno = err; + return (NULL); + } + + return (nvl); +} + +/* + * /devices/pci0@0/....@0,1:ppt -> /pci0@0/...@0,1 + */ +static const char * +fs_to_phys_path(char *fspath) +{ + const char prefix[] = "/devices"; + char *c; + + if ((c = strrchr(fspath, ':')) != NULL && strcmp(c, ":ppt") == 0) + *c = '\0'; + + c = fspath; + + if (strncmp(c, prefix, sizeof (prefix) - 1) == 0) + c += sizeof (prefix) - 1; + + return (c); +} + +/* + * Return an nvlist representing the mappings of /dev/ppt* devices to physical + * devices. Of the form: + * + * /pci@0,0/... { + * dev: "/dev/ppt0" + * path: "/pci@0,0/..." + * vendor-id: "8086" + * device-id: "1528" + * subsystem-vendor-id: "8086" + * subsystem-id: "1528" + * revision-id: "1" + * label: "Intel Corporation ..." + * }, + * /pci@0,0/... + * + * The nvlist should be freed by the caller. + */ +nvlist_t * +ppt_list_assigned(void) +{ + di_node_t di_root = DI_NODE_NIL; + pcidb_hdl_t *db = NULL; + nvlist_t *nvl = NULL; + glob_t gl; + int err; + + bzero(&gl, sizeof (gl)); + + if ((di_root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) + return (NULL); + + if ((db = pcidb_open(PCIDB_VERSION)) == NULL) { + err = errno; + goto out; + } + + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto out; + + if ((err = glob("/dev/ppt*", GLOB_KEEPSTAT | GLOB_ERR, + NULL, &gl)) != 0) { + err = (err == GLOB_NOMATCH) ? 0 : errno; + goto out; + } + + for (size_t i = 0; i < gl.gl_pathc; i++) { + char fspath[MAXPATHLEN]; + nvlist_t *info_nvl; + di_node_t di_node; + const char *path; + + if (!S_ISLNK(gl.gl_statv[i]->st_mode)) + continue; + + if (realpath(gl.gl_pathv[i], fspath) == NULL) { + err = errno; + goto out; + } + + path = fs_to_phys_path(fspath); + + /* + * path argument is treated as const. + */ + if ((di_node = di_lookup_node(di_root, (char *)path)) == NULL) { + err = errno; + goto out; + } + + if (!is_pci(di_node)) + continue; + + info_nvl = dev_getinfo(di_node, db, gl.gl_pathv[i], path); + + if (info_nvl == NULL) { + err = errno; + goto out; + } + + err = nvlist_add_nvlist(nvl, path, info_nvl); + nvlist_free(info_nvl); + + if (err) + goto out; + } + +out: + if (di_root != DI_NODE_NIL) + di_fini(di_root); + + pcidb_close(db); + globfree(&gl); + + if (err) { + nvlist_free(nvl); + errno = err; + return (NULL); + } + + return (nvl); +} + +/* + * Read in our list of potential PPT devices. A boot-module provided file + * explicitly over-rides anything delivered. + */ +static int +get_matches(list_t *listp) +{ + int err = 0; + FILE *fp; + + list_create(listp, sizeof (ppt_match_t), + offsetof(ppt_match_t, pm_list)); + + if ((fp = fopen("/system/boot/etc/ppt_matches", "r")) == NULL) { + if (errno != ENOENT) + return (errno); + + if ((fp = fopen("/etc/ppt_matches", "r")) == NULL) { + if (errno == ENOENT) + return (0); + return (errno); + } + } + + for (;;) { + char *line = NULL; + ppt_match_t *pm; + size_t cap = 0; + ssize_t read; + + if ((read = getline(&line, &cap, fp)) <= 0) { + free(line); + break; + } + + if (line[read - 1] == '\n') + line[read - 1] = '\0'; + + if ((pm = malloc(sizeof (*pm))) == NULL) { + err = errno; + free(line); + goto out; + } + + bzero(pm, sizeof (*pm)); + + if (sscanf(line, "pciex%4s,%4s", &pm->pm_vendor, + &pm->pm_device) == 2 || + sscanf(line, "pci%4s,%4s", &pm->pm_vendor, + &pm->pm_device) == 2 || + sscanf(line, "pciex%4s", &pm->pm_vendor) == 1 || + sscanf(line, "pci%4s", &pm->pm_vendor) == 1) { + list_insert_tail(listp, pm); + } else if (line[0] == '/') { + (void) strlcpy(pm->pm_path, line, sizeof (pm->pm_path)); + list_insert_tail(listp, pm); + } else { + /* + * Ignore any line we don't understand. + */ + free(pm); + } + + free(line); + } + +out: + (void) fclose(fp); + return (err); +} + +static boolean_t +match_ppt(list_t *matches, nvlist_t *nvl) +{ + char *vendor; + char *device; + char *path; + + if (nvlist_lookup_string(nvl, "path", &path) != 0 || + nvlist_lookup_string(nvl, "vendor-id", &vendor) != 0 || + nvlist_lookup_string(nvl, "device-id", &device) != 0) + return (B_FALSE); + + for (ppt_match_t *pm = list_head(matches); pm != NULL; + pm = list_next(matches, pm)) { + if (pm->pm_path[0] != '\0' && strcmp(pm->pm_path, path) == 0) + return (B_TRUE); + + if (pm->pm_vendor[0] != '\0' && + strcmp(pm->pm_vendor, vendor) == 0) { + if (pm->pm_device[0] == '\0') + return (B_TRUE); + if (strcmp(pm->pm_device, device) == 0) + return (B_TRUE); + } + } + + return (B_FALSE); +} + +static int +inspect_node(di_node_t di_node, void *arg) +{ + node_data_t *data = arg; + nvlist_t *info_nvl = NULL; + char *devname = NULL; + const char *driver; + char *path = NULL; + + if (!is_pci(di_node)) + return (DI_WALK_CONTINUE); + + driver = di_driver_name(di_node); + + if (driver != NULL && strcmp(driver, "ppt") == 0) { + if (asprintf(&devname, "/dev/ppt%d", + di_instance(di_node)) < 0) { + data->nd_err = errno; + goto out; + } + } + + if ((path = di_devfs_path(di_node)) == NULL) { + data->nd_err = ENOENT; + goto out; + } + + info_nvl = dev_getinfo(di_node, data->nd_db, devname, path); + + if (info_nvl == NULL) + goto out; + + if (devname == NULL && !match_ppt(&data->nd_matches, info_nvl)) + goto out; + + data->nd_err = nvlist_add_nvlist(data->nd_nvl, path, info_nvl); + +out: + free(path); + free(devname); + nvlist_free(info_nvl); + return (data->nd_err ? DI_WALK_TERMINATE : DI_WALK_CONTINUE); +} + +/* + * Like ppt_list_assigned() output, but includes all devices that could be used + * for passthrough, whether assigned or not. + */ +nvlist_t * +ppt_list(void) +{ + node_data_t nd = { NULL, }; + di_node_t di_root; + int err; + + if ((di_root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) + return (NULL); + + if ((err = get_matches(&nd.nd_matches)) != 0) + goto out; + + if ((nd.nd_db = pcidb_open(PCIDB_VERSION)) == NULL) { + err = errno; + goto out; + } + + if ((err = nvlist_alloc(&nd.nd_nvl, NV_UNIQUE_NAME, 0)) != 0) + goto out; + + if ((err = di_walk_node(di_root, DI_WALK_CLDFIRST, + &nd, inspect_node)) != 0) + goto out; + + err = nd.nd_err; + +out: + pcidb_close(nd.nd_db); + + for (ppt_match_t *pm = list_head(&nd.nd_matches); pm != NULL; ) { + ppt_match_t *next = list_next(&nd.nd_matches, pm); + free(pm); + pm = next; + } + + if (di_root != DI_NODE_NIL) + di_fini(di_root); + + if (err) { + nvlist_free(nd.nd_nvl); + errno = err; + return (NULL); + } + + return (nd.nd_nvl); +} + +/* + * Given a physical path such as "/devices/pci0@0...", return the "/dev/pptX" + * that is bound to it, if any. The "/devices/" prefix is optional. The + * physical path may have the ":ppt" minor name suffix. + * + * Returns ENOENT if no such PPT device exists. + */ +int +ppt_devpath_to_dev(const char *inpath, char *buf, size_t buflen) +{ + char fspath[MAXPATHLEN] = ""; + nvpair_t *nvp = NULL; + const char *devpath; + int err = ENOENT; + nvlist_t *nvl; + + if (strlcat(fspath, inpath, sizeof (fspath)) >= sizeof (fspath)) + return (ENAMETOOLONG); + + devpath = fs_to_phys_path(fspath); + + if ((nvl = ppt_list_assigned()) == NULL) + return (errno); + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + const char *name = nvpair_name(nvp); + char *ppt = NULL; + nvlist_t *props; + + (void) nvpair_value_nvlist(nvp, &props); + + if (strcmp(name, devpath) == 0) { + (void) nvlist_lookup_string(props, "dev", &ppt); + + err = 0; + + if (strlcpy(buf, ppt, buflen) >= buflen) + err = ENAMETOOLONG; + break; + } + } + + nvlist_free(nvl); + return (err); +} diff --git a/usr/src/lib/libppt/common/libppt.h b/usr/src/lib/libppt/common/libppt.h new file mode 100644 index 0000000000..efbf2c7b8b --- /dev/null +++ b/usr/src/lib/libppt/common/libppt.h @@ -0,0 +1,36 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * + * Copyright 2018 Joyent, Inc. + */ + +#ifndef _LIBPPT_H +#define _LIBPPT_H + +#include <sys/types.h> + +#include <libnvpair.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int ppt_devpath_to_dev(const char *, char *, size_t); + +extern nvlist_t *ppt_list_assigned(void); + +extern nvlist_t *ppt_list(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPPT_H */ diff --git a/usr/src/lib/libppt/common/llib-lppt b/usr/src/lib/libppt/common/llib-lppt new file mode 100644 index 0000000000..dadd992a31 --- /dev/null +++ b/usr/src/lib/libppt/common/llib-lppt @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libppt.h> diff --git a/usr/src/lib/libppt/common/mapfile-vers b/usr/src/lib/libppt/common/mapfile-vers new file mode 100644 index 0000000000..d9d882874b --- /dev/null +++ b/usr/src/lib/libppt/common/mapfile-vers @@ -0,0 +1,40 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOSprivate { + global: + ppt_devpath_to_dev; + ppt_list_assigned; + ppt_list; + + local: + *; +}; diff --git a/usr/src/lib/libppt/i386/Makefile b/usr/src/lib/libppt/i386/Makefile new file mode 100644 index 0000000000..3f11e556d4 --- /dev/null +++ b/usr/src/lib/libppt/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libppt/sparc/Makefile b/usr/src/lib/libppt/sparc/Makefile new file mode 100644 index 0000000000..3f11e556d4 --- /dev/null +++ b/usr/src/lib/libppt/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libppt/sparcv9/Makefile b/usr/src/lib/libppt/sparcv9/Makefile new file mode 100644 index 0000000000..5a304d7fe7 --- /dev/null +++ b/usr/src/lib/libppt/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile index e242b24eee..94d5d9bd4d 100644 --- a/usr/src/man/man1m/Makefile +++ b/usr/src/man/man1m/Makefile @@ -557,7 +557,8 @@ _MANFILES= 6to4relay.1m \ i386_MANFILES= \ acpidump.1m \ acpixtract.1m \ - nvmeadm.1m + nvmeadm.1m \ + pptadm.1m sparc_MANFILES= cvcd.1m \ dcs.1m \ diff --git a/usr/src/man/man1m/pptadm.1m b/usr/src/man/man1m/pptadm.1m new file mode 100644 index 0000000000..f13a5e32a4 --- /dev/null +++ b/usr/src/man/man1m/pptadm.1m @@ -0,0 +1,74 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" Copyright 2018 Joyent, Inc. +.\" +.Dd April 10, 2018 +.Dt PPTADM 1M +.Os +.Sh NAME +.Nm pptadm +.Nd PPT administration utility +.Sh SYNOPSIS +.Nm +.Cm list -j +.Op Fl a +.Nm +.Cm list +.Op Fl ap Op Fl o Ar fields +.Sh DESCRIPTION +The +.Nm +utility can enumerate passthrough devices for use by a virtualized guest. +.Sh OPTIONS +The following options to the +.Cm list +command are supported: +.Bl -tag -width Ds +.It Fl a +Show all PPT devices, both available and assigned. +.It Fl j +Output JSON. +.It Fl o +Specify fields to output, or "all". Available fields are +dev,path,vendor,device,subvendor,subdevice,rev,label +.It Fl p +Output in a parsable format; this requires the -o option to be specified. +.El +.Sh JSON OUTPUT +The JSON output consists of an array under the key "devices" with the fields: +.Bl -tag -width Ds +.It dev +The PPT /dev path, if assigned and bound. +.It path +The physical /devices path. +.It vendor-id +The PCI vendor ID. +.It device-id +The PCI device ID. +.It subsystem-vendor-id +The PCI subsystem vendor ID. +.It subsystem-id +The PCI subsystem ID. +.It revision-id +The PCI device revision. +.It label +Human-readable description from the PCI database. +.El +.Sh FILES +.Bl -tag -width Ds +.It /etc/ppt_aliases +Containts the bindings of PPT devices in the same format as /etc/driver_aliases +.It /etc/ppt_matches +Identifies devices that PPT could be bound to, either by physical path, or by +PCI ID. +.El +.Sh EXIT STATUS +.Ex -std diff --git a/usr/src/pkg/manifests/system-bhyve.mf b/usr/src/pkg/manifests/system-bhyve.mf index 4b95d3986a..fe19fb21b4 100644 --- a/usr/src/pkg/manifests/system-bhyve.mf +++ b/usr/src/pkg/manifests/system-bhyve.mf @@ -36,18 +36,25 @@ dir path=usr group=sys dir path=usr/kernel/drv group=sys dir path=usr/kernel/drv/$(ARCH64) group=sys dir path=usr/lib group=bin +dir path=usr/share +dir path=usr/share/man +dir path=usr/share/man/man1m dir path=usr/sbin driver name=ppt driver name=viona driver name=vmm file path=lib/$(ARCH64)/libvmmapi.so.1 +link path=lib/$(ARCH64)/libvmmapi.so target=./libvmmapi.so.1 file path=usr/kernel/drv/$(ARCH64)/ppt file path=usr/kernel/drv/$(ARCH64)/viona file path=usr/kernel/drv/$(ARCH64)/vmm file path=usr/kernel/drv/ppt.conf file path=usr/kernel/drv/viona.conf file path=usr/kernel/drv/vmm.conf +file path=usr/lib/libppt.so.1 +file path=usr/lib/$(ARCH64)/libppt.so.1 file path=usr/sbin/bhyve mode=0555 file path=usr/sbin/bhyvectl mode=0555 +file path=usr/sbin/pptadm mode=0555 +file path=usr/share/man/man1m/pptadm.1m license lic_CDDL license=lic_CDDL -link path=lib/$(ARCH64)/libvmmapi.so target=./libvmmapi.so.1 diff --git a/usr/src/uts/common/os/modsysfile.c b/usr/src/uts/common/os/modsysfile.c index 8dca86880f..37ac089edf 100644 --- a/usr/src/uts/common/os/modsysfile.c +++ b/usr/src/uts/common/os/modsysfile.c @@ -23,6 +23,7 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2016 Nexenta Systems, Inc. + * Copyright 2018 Joyent, Inc. */ #include <sys/types.h> @@ -57,10 +58,12 @@ struct hwc_class *hcl_head; /* head of list of classes */ static kmutex_t hcl_lock; /* for accessing list of classes */ #define DAFILE "/etc/driver_aliases" +#define PPTFILE "/etc/ppt_aliases" #define CLASSFILE "/etc/driver_classes" #define DACFFILE "/etc/dacf.conf" static char class_file[] = CLASSFILE; +static char pptfile[] = PPTFILE; static char dafile[] = DAFILE; static char dacffile[] = DACFFILE; @@ -2136,14 +2139,13 @@ hwc_parse_now(char *fname, struct par_list **pl, ddi_prop_t **props) return (0); /* always return success */ } -void -make_aliases(struct bind **bhash) +static void +parse_aliases(struct bind **bhash, struct _buf *file) { enum { AL_NEW, AL_DRVNAME, AL_DRVNAME_COMMA, AL_ALIAS, AL_ALIAS_COMMA } state; - struct _buf *file; char tokbuf[MAXPATHLEN]; char drvbuf[MAXPATHLEN]; token_t token; @@ -2152,9 +2154,6 @@ make_aliases(struct bind **bhash) static char dupwarn[] = "!Driver alias \"%s\" conflicts with " "an existing driver name or alias."; - if ((file = kobj_open_file(dafile)) == (struct _buf *)-1) - return; - state = AL_NEW; major = DDI_MAJOR_T_NONE; while (!done) { @@ -2239,8 +2238,22 @@ make_aliases(struct bind **bhash) kobj_file_err(CE_WARN, file, tok_err, tokbuf); } } +} - kobj_close_file(file); +void +make_aliases(struct bind **bhash) +{ + struct _buf *file; + + if ((file = kobj_open_file(pptfile)) != (struct _buf *)-1) { + parse_aliases(bhash, file); + kobj_close_file(file); + } + + if ((file = kobj_open_file(dafile)) != (struct _buf *)-1) { + parse_aliases(bhash, file); + kobj_close_file(file); + } } |