summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJohn Levon <john.levon@joyent.com>2020-04-07 11:56:51 +0100
committerGitHub <noreply@github.com>2020-04-07 11:56:51 +0100
commit535049543b46f738d02d1324951d3d55133b67c0 (patch)
tree4201add9116ab9974b3e2e0c3e4d3500db2b48a6 /usr/src
parentfaf4cb6b3e6bd53126eea31040e912d0c5f23d3c (diff)
parent3771a01f34a93c69e6c4ef22870fda7b502390a0 (diff)
downloadillumos-joyent-OS-8138.tar.gz
Merge branch 'master' into OS-8138OS-8138
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/brand/bhyve/zone/boot.c47
-rw-r--r--usr/src/lib/fm/topo/libtopo/Makefile.com4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/libtopo.h34
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mapfile-vers16
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_builtin.c79
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_builtin.h9
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_digraph.c1035
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_digraph.h64
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.c1502
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.h88
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_fmri.c72
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_method.h5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h16
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.map8
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_node.c22
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_snap.c36
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_subr.c9
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_tree.h4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_xml.c10
-rw-r--r--usr/src/lib/fm/topo/maps/Makefile.map17
-rw-r--r--usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.152
-rw-r--r--usr/src/lib/fm/topo/modules/common/ses/ses.c6
-rw-r--r--usr/src/lib/libpcsc/sparc/Makefile18
-rw-r--r--usr/src/lib/libpcsc/sparcv9/Makefile19
-rw-r--r--usr/src/man/man3kstat/kstat_chain_update.3kstat19
-rw-r--r--usr/src/man/man3kstat/kstat_lookup.3kstat22
-rw-r--r--usr/src/man/man7d/amdf17nbdf.7d4
-rw-r--r--usr/src/pkg/manifests/driver-cpu-sensor.mf7
-rw-r--r--usr/src/pkg/manifests/service-fault-management.mf4
-rw-r--r--usr/src/pkg/manifests/system-header.mf2
-rw-r--r--usr/src/pkg/manifests/system-test-ostest.mf7
-rw-r--r--usr/src/test/os-tests/runfiles/default.run4
-rw-r--r--usr/src/test/os-tests/tests/Makefile1
-rw-r--r--usr/src/test/os-tests/tests/libtopo/Makefile58
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test-in-badedge.xml46
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test-in-badelement.xml42
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test-in-badnum.xml42
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test-in-badscheme.xml42
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test-in.xml308
-rw-r--r--usr/src/test/os-tests/tests/libtopo/digraph-test.c380
-rw-r--r--usr/src/uts/common/krtld/reloc.h6
-rw-r--r--usr/src/uts/common/sys/fm/protocol.h11
-rw-r--r--usr/src/uts/i86pc/os/cpuid_subr.c66
-rw-r--r--usr/src/uts/intel/ia32/sys/Makefile6
-rw-r--r--usr/src/uts/intel/ia32/sys/kdi_regs.h73
-rw-r--r--usr/src/uts/intel/ia32/sys/privmregs.h67
-rw-r--r--usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c31
-rw-r--r--usr/src/uts/intel/os/driver_aliases5
-rw-r--r--usr/src/uts/intel/sys/x86_archext.h18
-rw-r--r--usr/src/uts/sparc/krtld/kobj_reloc.c13
51 files changed, 4329 insertions, 131 deletions
diff --git a/usr/src/lib/brand/bhyve/zone/boot.c b/usr/src/lib/brand/bhyve/zone/boot.c
index dadcf4e96a..645958d14a 100644
--- a/usr/src/lib/brand/bhyve/zone/boot.c
+++ b/usr/src/lib/brand/bhyve/zone/boot.c
@@ -225,11 +225,17 @@ add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len)
static char *boot = NULL;
static int next_cd = 0;
static int next_other = 0;
+ custr_t *sconfstr = NULL;
const char *model = "virtio-blk";
uint_t pcibus = 0, pcidev = 0, pcifn = 0;
const char *slotstr;
- const char *nodelstr = "";
+ const char *guest_block_size = NULL;
boolean_t isboot;
+ boolean_t nodelete = B_FALSE;
+
+ if (custr_alloc_buf(&sconfstr, slotconf, slotconf_len) == -1) {
+ return (-1);
+ }
isboot = is_env_true("device", disk, "boot");
if (isboot) {
@@ -237,14 +243,14 @@ add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len)
if (boot != NULL) {
(void) printf("Error: multiple boot disks: %s %s\n",
boot, path);
- return (-1);
+ goto fail;
}
boot = path;
}
if ((slotstr = get_zcfg_var("device", disk, "pci_slot")) != NULL) {
if (parse_pcislot(slotstr, &pcibus, &pcidev, &pcifn) != 0) {
- return (-1);
+ goto fail;
}
} else {
if (isboot) {
@@ -271,8 +277,15 @@ add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len)
* be the most familiar name for the operation (and less
* likely to cause confusion).
*/
- if (is_env_string("device", disk, "notrim", "true"))
- nodelstr = ",nodelete";
+ nodelete = is_env_true("device", disk, "notrim");
+ guest_block_size = get_zcfg_var("device", disk,
+ "guest_block_size");
+
+ /* Treat a 0 size to mean the whatever the volume advertises */
+ if (guest_block_size != NULL &&
+ strcmp(guest_block_size, "0") == 0) {
+ guest_block_size = NULL;
+ }
} else if (is_env_string("device", disk, "model", "ahci")) {
if (is_env_string("device", disk, "media", "cdrom")) {
model = "ahci-cd";
@@ -281,16 +294,32 @@ add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len)
}
} else {
(void) printf("Error: unknown disk model '%s'\n", model);
- return (-1);
+ goto fail;
}
- if (snprintf(slotconf, slotconf_len, "%u:%u:%u,%s,%s%s",
- pcibus, pcidev, pcifn, model, path, nodelstr) >= slotconf_len) {
+ if (custr_append_printf(sconfstr, "%u:%u:%u,%s,%s",
+ pcibus, pcidev, pcifn, model, path) == -1) {
(void) printf("Error: disk path '%s' too long\n", path);
- return (-1);
+ goto fail;
+ }
+
+ if (nodelete && custr_append(sconfstr, ",nodelete") == -1) {
+ (void) printf("Error: too many disk options\n");
+ goto fail;
+ }
+
+ if (guest_block_size != NULL && custr_append_printf(sconfstr,
+ ",sectorsize=%s", guest_block_size) == -1) {
+ (void) printf("Error: too many disk options\n");
+ goto fail;
}
+ custr_free(sconfstr);
return (0);
+
+fail:
+ custr_free(sconfstr);
+ return (-1);
}
static int
diff --git a/usr/src/lib/fm/topo/libtopo/Makefile.com b/usr/src/lib/fm/topo/libtopo/Makefile.com
index e3b18ffa3a..8592fc9881 100644
--- a/usr/src/lib/fm/topo/libtopo/Makefile.com
+++ b/usr/src/lib/fm/topo/libtopo/Makefile.com
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
LIBRARY = libtopo.a
@@ -43,6 +43,8 @@ LIBSRCS = \
topo_2xml.c \
topo_alloc.c \
topo_builtin.c \
+ topo_digraph.c \
+ topo_digraph_xml.c \
topo_error.c \
topo_file.c \
topo_fmri.c \
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c
index cb53f87a51..6c527fcc8c 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c
@@ -1138,7 +1138,7 @@ hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
int ret;
nvlist_t *args, *pfmri = NULL;
nvlist_t *auth;
- uint32_t inst;
+ uint64_t inst;
char *name, *serial, *rev, *part;
if (version > TOPO_METH_FMRI_VERSION)
@@ -1147,7 +1147,7 @@ hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
/* First the must-have fields */
if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
- if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
+ if (nvlist_lookup_uint64(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
/*
diff --git a/usr/src/lib/fm/topo/libtopo/common/libtopo.h b/usr/src/lib/fm/topo/libtopo/common/libtopo.h
index 584ac279bf..02d7828a79 100644
--- a/usr/src/lib/fm/topo/libtopo/common/libtopo.h
+++ b/usr/src/lib/fm/topo/libtopo/common/libtopo.h
@@ -42,7 +42,7 @@ extern "C" {
typedef struct topo_hdl topo_hdl_t;
typedef struct topo_node tnode_t;
typedef struct topo_walk topo_walk_t;
-typedef int32_t topo_instance_t;
+typedef uint64_t topo_instance_t;
typedef uint32_t topo_version_t;
typedef struct topo_list {
@@ -55,6 +55,21 @@ typedef struct topo_faclist {
tnode_t *tf_node;
} topo_faclist_t;
+typedef struct topo_digraph topo_digraph_t;
+typedef struct topo_vertex topo_vertex_t;
+typedef struct topo_edge topo_edge_t;
+
+typedef struct topo_path {
+ const char *tsp_fmristr;
+ nvlist_t *tsp_fmri;
+ topo_list_t tsp_components;
+} topo_path_t;
+
+typedef struct topo_path_component {
+ topo_list_t tspc_link;
+ topo_vertex_t *tspc_vertex;
+} topo_path_component_t;
+
/*
* The following functions, error codes and data structures are private
* to libtopo snapshot consumers and enumerator modules.
@@ -399,6 +414,23 @@ extern char *topo_hdl_strsplit(topo_hdl_t *, const char *, const char *,
char **);
/*
+ * Interfaces for interacting with directed graph topologies
+ */
+extern topo_digraph_t *topo_digraph_get(topo_hdl_t *, const char *);
+extern int topo_vertex_iter(topo_hdl_t *, topo_digraph_t *,
+ int (*)(topo_hdl_t *, topo_vertex_t *, boolean_t, void *), void *);
+extern tnode_t *topo_vertex_node(topo_vertex_t *);
+extern int topo_edge_iter(topo_hdl_t *, topo_vertex_t *,
+ int (*)(topo_hdl_t *, topo_edge_t *, boolean_t, void *), void *);
+extern int topo_digraph_paths(topo_hdl_t *, topo_digraph_t *,
+ topo_vertex_t *, topo_vertex_t *, topo_path_t ***, uint_t *);
+extern void topo_path_destroy(topo_hdl_t *, topo_path_t *);
+extern int topo_digraph_serialize(topo_hdl_t *, topo_digraph_t *, FILE *);
+extern topo_digraph_t *topo_digraph_deserialize(topo_hdl_t *, const char *,
+ size_t);
+extern topo_vertex_t *topo_node_vertex(tnode_t *);
+
+/*
* Interfaces for converting sensor/indicator types, units, states, etc to
* a string
*/
diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
index 8904c17ac5..ca33084ff2 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
+++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
#
@@ -43,6 +43,14 @@ SYMBOL_VERSION SUNWprivate {
global:
topo_close;
topo_debug_set;
+ topo_digraph_deserialize;
+ topo_digraph_destroy;
+ topo_digraph_get;
+ topo_digraph_new;
+ topo_digraph_paths;
+ topo_digraph_serialize;
+ topo_edge_iter;
+ topo_edge_new;
topo_fmri_asru;
topo_fmri_compare;
topo_fmri_contains;
@@ -160,7 +168,9 @@ SYMBOL_VERSION SUNWprivate {
topo_node_resource;
topo_node_setspecific;
topo_node_unbind;
+ topo_node_vertex;
topo_open;
+ topo_path_destroy;
topo_pgroup_create;
topo_pgroup_destroy;
topo_pgroup_info;
@@ -208,6 +218,10 @@ SYMBOL_VERSION SUNWprivate {
topo_snap_hold;
topo_snap_release;
topo_strerror;
+ topo_vertex_destroy;
+ topo_vertex_iter;
+ topo_vertex_node;
+ topo_vertex_new;
topo_walk_fini;
topo_walk_init;
topo_walk_step;
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
index fe979488bd..8acb9cf453 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
*/
#include <pthread.h>
@@ -42,17 +43,18 @@
#include <zfs.h>
static const struct topo_builtin _topo_builtins[] = {
- { "cpu", CPU_VERSION, cpu_init, cpu_fini },
- { "dev", DEV_VERSION, dev_init, dev_fini },
- { "fmd", FMD_VERSION, fmd_init, fmd_fini },
- { "mem", MEM_VERSION, mem_init, mem_fini },
- { "pkg", PKG_VERSION, pkg_init, pkg_fini },
- { "svc", SVC_VERSION, svc_init, svc_fini },
- { "sw", SW_VERSION, sw_init, sw_fini },
- { "zfs", ZFS_VERSION, zfs_init, zfs_fini },
- { "mod", MOD_VERSION, mod_init, mod_fini },
- { "hc", HC_VERSION, hc_init, hc_fini }, /* hc must go last */
- { NULL, 0, NULL, NULL }
+ { "cpu", CPU_VERSION, cpu_init, cpu_fini, TOPO_BLTIN_TYPE_TREE },
+ { "dev", DEV_VERSION, dev_init, dev_fini, TOPO_BLTIN_TYPE_TREE },
+ { "fmd", FMD_VERSION, fmd_init, fmd_fini, TOPO_BLTIN_TYPE_TREE },
+ { "mem", MEM_VERSION, mem_init, mem_fini, TOPO_BLTIN_TYPE_TREE },
+ { "pkg", PKG_VERSION, pkg_init, pkg_fini, TOPO_BLTIN_TYPE_TREE },
+ { "svc", SVC_VERSION, svc_init, svc_fini, TOPO_BLTIN_TYPE_TREE },
+ { "sw", SW_VERSION, sw_init, sw_fini, TOPO_BLTIN_TYPE_TREE },
+ { "zfs", ZFS_VERSION, zfs_init, zfs_fini, TOPO_BLTIN_TYPE_TREE },
+ { "mod", MOD_VERSION, mod_init, mod_fini, TOPO_BLTIN_TYPE_TREE },
+ /* hc must go last */
+ { "hc", HC_VERSION, hc_init, hc_fini, TOPO_BLTIN_TYPE_TREE },
+ { NULL, 0, NULL, NULL, 0 }
};
static int
@@ -105,6 +107,7 @@ topo_builtin_create(topo_hdl_t *thp, const char *rootdir)
topo_mod_t *mod;
ttree_t *tp;
tnode_t *rnode;
+ topo_digraph_t *tdg;
/*
* Create a scheme-specific topo tree for all builtins
@@ -118,35 +121,49 @@ topo_builtin_create(topo_hdl_t *thp, const char *rootdir)
&topo_bltin_ops, bp->bltin_version)) == NULL) {
topo_dprintf(thp, TOPO_DBG_ERR,
"unable to create scheme "
- "tree for %s:%s\n", bp->bltin_name,
+ "topology for %s:%s\n", bp->bltin_name,
topo_hdl_errmsg(thp));
return (-1);
}
- if ((tp = topo_tree_create(thp, mod, bp->bltin_name))
- == NULL) {
- topo_dprintf(thp, TOPO_DBG_ERR,
- "unable to create scheme "
- "tree for %s:%s\n", bp->bltin_name,
- topo_hdl_errmsg(thp));
+ switch (bp->bltin_type) {
+ case TOPO_BLTIN_TYPE_TREE:
+ if ((tp = topo_tree_create(thp, mod, bp->bltin_name))
+ == NULL) {
+ topo_dprintf(thp, TOPO_DBG_ERR, "unable to "
+ "create scheme tree for %s:%s\n",
+ bp->bltin_name, topo_hdl_errmsg(thp));
+ return (-1);
+ }
+ topo_list_append(&thp->th_trees, tp);
+
+ rnode = tp->tt_root;
+ break;
+ case TOPO_BLTIN_TYPE_DIGRAPH:
+ if ((tdg = topo_digraph_new(thp, mod, bp->bltin_name))
+ == NULL) {
+ topo_dprintf(thp, TOPO_DBG_ERR, "unable to "
+ "create scheme digraph for %s:%s\n",
+ bp->bltin_name, topo_hdl_errmsg(thp));
+ return (-1);
+ }
+ topo_list_append(&thp->th_digraphs, tdg);
+
+ rnode = tdg->tdg_rootnode;
+ break;
+ default:
+ topo_dprintf(thp, TOPO_DBG_ERR, "unexpected topology "
+ "type: %u", bp->bltin_type);
return (-1);
}
- topo_list_append(&thp->th_trees, tp);
-
- /*
- * Call the enumerator on the root of the tree, with the
- * scheme name as the name to enumerate. This will
- * establish methods on the root node.
- */
- rnode = tp->tt_root;
- if (topo_mod_enumerate(mod, rnode, mod->tm_name, rnode->tn_name,
- rnode->tn_instance, rnode->tn_instance, NULL) < 0) {
+ if (topo_mod_enumerate(mod, rnode, mod->tm_name,
+ rnode->tn_name, rnode->tn_instance, rnode->tn_instance,
+ NULL) < 0) {
/*
- * If we see a failure, note it in the handle and
- * drive on
+ * If we see a failure, note it in the handle and drive
+ * on
*/
(void) topo_hdl_seterrno(thp, ETOPO_ENUM_PARTIAL);
}
-
}
return (0);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.h b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.h
index 8d34f643d0..7a387c9c51 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.h
@@ -23,19 +23,23 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
#ifndef _TOPO_BUILTIN_H
#define _TOPO_BUILTIN_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
#include <topo_tree.h>
#include <topo_module.h>
+#include <topo_digraph.h>
+#define TOPO_BLTIN_TYPE_TREE 1
+#define TOPO_BLTIN_TYPE_DIGRAPH 2
/*
* topo_builtin.h
*
@@ -50,6 +54,7 @@ typedef struct topo_builtin {
topo_version_t bltin_version;
int (*bltin_init)(topo_mod_t *, topo_version_t version);
void (*bltin_fini)(topo_mod_t *);
+ uint_t bltin_type;
} topo_builtin_t;
extern int topo_builtin_create(topo_hdl_t *, const char *);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_digraph.c b/usr/src/lib/fm/topo/libtopo/common/topo_digraph.c
new file mode 100644
index 0000000000..8f66794ff5
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_digraph.c
@@ -0,0 +1,1035 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+/*
+ * This file implements a set of APIs for creating, destroying and traversing
+ * directed-graph (digraph) topologies. The API is split into categories:
+ * client API and module API.
+ *
+ * The client API can be used by libtopo consumers for traversing an existing
+ * digraph topology. The module API can be used by topo plugins to create or
+ * destroy a digraph topology.
+ *
+ * client API
+ * ----------
+ * topo_digraph_get
+ * topo_vertex_iter
+ * topo_vertex_node
+ * topo_edge_iter
+ * topo_digraph_paths
+ * topo_path_destroy
+ *
+ * module API
+ * ----------
+ * topo_digraph_new
+ * topo_digraph_destroy
+ * topo_vertex_new
+ * topo_vertex_destroy
+ * topo_edge_new
+ *
+ * A digraph is represented in-core by a topo_digraph_t structure which
+ * maintains an adjacency list of vertices (see topo_digraph.h). Vertices
+ * are represented by topo_vertex_t structures which are essentially thin
+ * wrappers around topo node (tnode_t) structures. In addition to holding
+ * a pointer to the underlyng topo node, the topo_vertex_t maintains a list
+ * of incoming and outgoing edges and a pointer to the next and previous
+ * vertices in digraph's adjecency list.
+ *
+ * Locking
+ * -------
+ * The module APIs should only be used during snapshot creation, which is
+ * single-threaded, or as the result of a call to topo_snap_release() or
+ * topo_close(). While libtopo does prevent concurrent calls to
+ * topo_snap_release() and topo_close() via the topo_hdl_t lock, there is no
+ * mechanism currently that prevents the situation where one or more threads
+ * were to access the snapshot (e.g. they've got a cached tnode_t ptr or are
+ * walking the snapshot) while another thread is calling topo_snap_release()
+ * or topo_close(). The same is true for tree topologies. It is up to the
+ * library consumer to provide their own synchronization or reference counting
+ * mechanism for that case. For example, fmd implements a reference counting
+ * mechanism around topo snapshots so that individual fmd modules can safely
+ * cache pointers to a given topo snapshot.
+ *
+ * None of the client APIs modify the state of the graph structure once
+ * the snapshot is created. The exception is the state of the underlyng topo
+ * nodes for each vertex, who's properties may change as a result of the
+ * following:
+ *
+ * 1) topo_prop_get operations for properties that are backed by topo methods
+ * 2) topo_prop_set operations.
+ *
+ * For both of the above situations, synchronization is enforced by the per-node
+ * locks. Thus there a no locks used for synchronizing access to
+ * the topo_digraph_t or topo_vertex_t structures.
+ *
+ * path scheme FMRIs
+ * -----------------
+ * For digraph topologies it is useful to be able to treat paths between
+ * vertices as resources and thus have a way to represent a unique path
+ * between those vertices. The path FMRI scheme is used for this purpose and
+ * has the following form:
+ *
+ * path://scheme=<scheme>/<nodename>=<instance>/...
+ *
+ * The path FMRI for a path from one vertex to another vertex is represented by
+ * a sequence of nodename/instance pairs where each pair represents a vertex on
+ * the path between the two vertices. The first nodename/instance pair
+ * represents the "from" vertex and the last nodename/instance pair represents
+ * the "to" vertex.
+ *
+ * For example, the path FMRI to represent a path from an initiator to a
+ * target in a SAS scheme digraph might look like this:
+ *
+ * path://scheme=sas/initiator=5003048023567a00/port=5003048023567a00/
+ * port=500304801861347f/expander=500304801861347f/port=500304801861347f/
+ * port=5000c500adc881d5/target=5000c500adc881d4
+ *
+ * This file implements NVL2STR and STR2NVL methods for path-scheme FMRIs.
+ */
+
+#include <libtopo.h>
+#include <sys/fm/protocol.h>
+
+#include <topo_digraph.h>
+#include <topo_method.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+
+extern int path_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
+extern int path_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
+
+static const topo_method_t digraph_root_methods[] = {
+ { TOPO_METH_PATH_STR2NVL, TOPO_METH_STR2NVL_DESC,
+ TOPO_METH_STR2NVL_VERSION, TOPO_STABILITY_INTERNAL,
+ path_fmri_str2nvl },
+ { TOPO_METH_PATH_NVL2STR, TOPO_METH_NVL2STR_DESC,
+ TOPO_METH_NVL2STR_VERSION, TOPO_STABILITY_INTERNAL,
+ path_fmri_nvl2str },
+ { NULL }
+};
+
+/*
+ * On success, returns a pointer to the digraph for the specified FMRI scheme.
+ * On failure, returns NULL.
+ */
+topo_digraph_t *
+topo_digraph_get(topo_hdl_t *thp, const char *scheme)
+{
+ topo_digraph_t *tdg;
+
+ for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL;
+ tdg = topo_list_next(tdg)) {
+ if (strcmp(scheme, tdg->tdg_scheme) == 0)
+ return (tdg);
+ }
+ return (NULL);
+}
+
+static topo_digraph_t *
+find_digraph(topo_mod_t *mod)
+{
+ return (topo_digraph_get(mod->tm_hdl, mod->tm_info->tmi_scheme));
+}
+
+/*
+ * On successs, allocates a new topo_digraph_t structure for the requested
+ * scheme. The caller is responsible for free all memory allocated for this
+ * structure when done via a call to topo_digraph_destroy().
+ *
+ * On failure, this function returns NULL and sets topo errno.
+ */
+topo_digraph_t *
+topo_digraph_new(topo_hdl_t *thp, topo_mod_t *mod, const char *scheme)
+{
+ topo_digraph_t *tdg;
+ tnode_t *tn = NULL;
+
+ if ((tdg = topo_mod_zalloc(mod, sizeof (topo_digraph_t))) == NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ return (NULL);
+ }
+
+ tdg->tdg_mod = mod;
+
+ if ((tdg->tdg_scheme = topo_mod_strdup(mod, scheme)) == NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ goto err;
+ }
+
+ /*
+ * For digraph topologies, the "root" node, which gets passed in to
+ * the scheme module's enum method is not part of the actual graph
+ * structure per-se.
+ * Its purpose is simply to provide a place on which to register the
+ * scheme-specific methods. Client code then invokes these methods via
+ * the topo_fmri_* interfaces.
+ */
+ if ((tn = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL)
+ goto err;
+
+ /*
+ * Adding the TOPO_NODE_ROOT state to the node has the effect of
+ * preventing topo_node_destroy() from trying to clean up the parent
+ * node's node hash, which is only necessary in tree topologies.
+ */
+ tn->tn_state = TOPO_NODE_ROOT | TOPO_NODE_INIT;
+ tn->tn_name = (char *)scheme;
+ tn->tn_instance = 0;
+ tn->tn_enum = mod;
+ tn->tn_hdl = thp;
+ topo_node_hold(tn);
+
+ tdg->tdg_rootnode = tn;
+ if (topo_method_register(mod, tn, digraph_root_methods) != 0) {
+ topo_mod_dprintf(mod, "failed to register digraph root "
+ "methods");
+ /* errno set */
+ return (NULL);
+ }
+
+ /* This is released during topo_digraph_destroy() */
+ topo_mod_hold(mod);
+
+ return (tdg);
+err:
+ topo_mod_free(mod, tdg, sizeof (topo_digraph_t));
+ return (NULL);
+}
+
+/*
+ * Deallocates all memory associated with the specified topo_digraph_t.
+ *
+ * This only frees the memory allocated during topo_digraph_new(). To free the
+ * actual graph vertices one should call topo_snap_destroy() prior to calling
+ * topo_digraph_destroy().
+ *
+ * Calling topo_close() will also result in a call to topo_snap_destroy() and
+ * topo_digraph_dstroy() for all digraphs associated with the library handle.
+ *
+ * This function is a NOP if NULL is passed in.
+ */
+void
+topo_digraph_destroy(topo_digraph_t *tdg)
+{
+ topo_mod_t *mod;
+
+ if (tdg == NULL)
+ return;
+
+ mod = tdg->tdg_mod;
+ topo_method_unregister_all(mod, tdg->tdg_rootnode);
+ topo_mod_strfree(mod, (char *)tdg->tdg_scheme);
+ topo_mod_free(mod, tdg->tdg_rootnode, sizeof (tnode_t));
+ topo_mod_free(mod, tdg, sizeof (topo_digraph_t));
+ topo_mod_rele(mod);
+}
+
+/*
+ * This function creates a new vertex and adds it to the digraph associated
+ * with the module.
+ *
+ * name: name of the vertex
+ * instance: instance number of the vertex
+ *
+ * On success, it returns a pointer to the allocated topo_vertex_t associated
+ * with the new vertex. The caller is responsible for free-ing this structure
+ * via a call to topo_vertex_destroy().
+ *
+ * On failures, this function returns NULL and sets topo_mod_errno.
+ */
+topo_vertex_t *
+topo_vertex_new(topo_mod_t *mod, const char *name, topo_instance_t inst)
+{
+ tnode_t *tn = NULL;
+ topo_vertex_t *vtx = NULL;
+ topo_digraph_t *tdg;
+
+ topo_mod_dprintf(mod, "Creating vertex %s=%" PRIx64 "", name, inst);
+ if ((tdg = find_digraph(mod)) == NULL) {
+ topo_mod_dprintf(mod, "%s faild: no existing digraph for FMRI "
+ " scheme %s", __func__, mod->tm_info->tmi_scheme);
+ return (NULL);
+ }
+ if ((vtx = topo_mod_zalloc(mod, sizeof (topo_vertex_t))) == NULL ||
+ (tn = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ if ((tn->tn_name = topo_mod_strdup(mod, name)) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ tn->tn_enum = mod;
+ tn->tn_hdl = mod->tm_hdl;
+ tn->tn_vtx = vtx;
+ tn->tn_instance = inst;
+ /*
+ * Adding the TOPO_NODE_ROOT state to the node has the effect of
+ * preventing topo_node_destroy() from trying to clean up the parent
+ * node's node hash, which is only necessary in tree topologies.
+ */
+ tn->tn_state = TOPO_NODE_ROOT | TOPO_NODE_BOUND;
+ vtx->tvt_node = tn;
+ topo_node_hold(tn);
+
+ /* Bump the refcnt on the module that's creating this vertex. */
+ topo_mod_hold(mod);
+
+ if (tdg->tdg_nvertices == UINT32_MAX) {
+ topo_mod_dprintf(mod, "Max vertices reached!");
+ (void) topo_mod_seterrno(mod, EMOD_DIGRAPH_MAXSZ);
+ topo_mod_rele(mod);
+ goto err;
+ }
+ tdg->tdg_nvertices++;
+ topo_list_append(&tdg->tdg_vertices, vtx);
+
+ return (vtx);
+err:
+ topo_mod_dprintf(mod, "failed to add create vertex %s=%" PRIx64 "(%s)",
+ name, inst, topo_strerror(topo_mod_errno(mod)));
+ if (tn != NULL) {
+ topo_mod_strfree(mod, tn->tn_name);
+ topo_mod_free(mod, tn, sizeof (tnode_t));
+ }
+ if (vtx != NULL)
+ topo_mod_free(mod, vtx, sizeof (topo_vertex_t));
+
+ return (NULL);
+}
+
+/*
+ * Returns the underlying tnode_t structure for the specified vertex.
+ */
+tnode_t *
+topo_vertex_node(topo_vertex_t *vtx)
+{
+ return (vtx->tvt_node);
+}
+
+/*
+ * Convenience interface for deallocating a topo_vertex_t
+ * This function is a NOP if NULL is passed in.
+ */
+void
+topo_vertex_destroy(topo_mod_t *mod, topo_vertex_t *vtx)
+{
+ topo_edge_t *edge;
+
+ if (vtx == NULL)
+ return;
+
+ topo_node_unbind(vtx->tvt_node);
+
+ edge = topo_list_next(&vtx->tvt_incoming);
+ while (edge != NULL) {
+ topo_edge_t *tmp = edge;
+
+ edge = topo_list_next(edge);
+ topo_mod_free(mod, tmp, sizeof (topo_edge_t));
+ }
+
+ edge = topo_list_next(&vtx->tvt_outgoing);
+ while (edge != NULL) {
+ topo_edge_t *tmp = edge;
+
+ edge = topo_list_next(edge);
+ topo_mod_free(mod, tmp, sizeof (topo_edge_t));
+ }
+
+ topo_mod_free(mod, vtx, sizeof (topo_vertex_t));
+}
+
+/*
+ * This function can be used to iterate over all of the vertices in the
+ * specified digraph. The specified callback function is invoked for each
+ * vertices. Callback function should return the standard topo walker
+ * (TOPO_WALK_*) return values.
+ *
+ * On success, this function returns 0.
+ *
+ * On failure this function returns -1.
+ */
+int
+topo_vertex_iter(topo_hdl_t *thp, topo_digraph_t *tdg,
+ int (*func)(topo_hdl_t *, topo_vertex_t *, boolean_t, void *), void *arg)
+{
+ uint_t n = 0;
+
+ for (topo_vertex_t *vtx = topo_list_next(&tdg->tdg_vertices);
+ vtx != NULL; vtx = topo_list_next(vtx), n++) {
+ int ret;
+ boolean_t last_vtx = B_FALSE;
+
+ if (n == (tdg->tdg_nvertices - 1))
+ last_vtx = B_TRUE;
+
+ ret = func(thp, vtx, last_vtx, arg);
+
+ switch (ret) {
+ case TOPO_WALK_NEXT:
+ continue;
+ case TOPO_WALK_TERMINATE:
+ goto out;
+ case TOPO_WALK_ERR:
+ default:
+ return (-1);
+ }
+ }
+out:
+ return (0);
+}
+
+/*
+ * Add a new outgoing edge the vertex "from" to the vertex "to".
+ *
+ * On success, this functions returns 0.
+ *
+ * On failure, this function returns -1.
+ */
+int
+topo_edge_new(topo_mod_t *mod, topo_vertex_t *from, topo_vertex_t *to)
+{
+ topo_digraph_t *tdg;
+ topo_edge_t *e_from = NULL, *e_to = NULL;
+
+ topo_mod_dprintf(mod, "Adding edge from vertex %s=%" PRIx64 " to "
+ "%s=%" PRIx64"", topo_node_name(from->tvt_node),
+ topo_node_instance(from->tvt_node),
+ topo_node_name(to->tvt_node), topo_node_instance(to->tvt_node));
+
+ if ((tdg = find_digraph(mod)) == NULL) {
+ topo_mod_dprintf(mod, "Digraph lookup failed");
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+ if (from->tvt_noutgoing == UINT32_MAX ||
+ to->tvt_nincoming == UINT32_MAX ||
+ tdg->tdg_nedges == UINT32_MAX) {
+ topo_mod_dprintf(mod, "Max edges reached!");
+ return (topo_mod_seterrno(mod, EMOD_DIGRAPH_MAXSZ));
+
+ }
+ if ((e_from = topo_mod_zalloc(mod, sizeof (topo_edge_t))) == NULL ||
+ (e_to = topo_mod_zalloc(mod, sizeof (topo_edge_t))) == NULL) {
+ topo_mod_free(mod, e_from, sizeof (topo_edge_t));
+ topo_mod_free(mod, e_to, sizeof (topo_edge_t));
+ return (topo_mod_seterrno(mod, EMOD_NOMEM));
+ }
+ e_from->tve_vertex = from;
+ e_to->tve_vertex = to;
+
+ topo_list_append(&from->tvt_outgoing, e_to);
+ from->tvt_noutgoing++;
+ topo_list_append(&to->tvt_incoming, e_from);
+ to->tvt_nincoming++;
+ tdg->tdg_nedges++;
+
+ return (0);
+}
+
+/*
+ * This function can be used to iterate over all of the outgoing edges in
+ * for specified vertex. The specified callback function is invoked for each
+ * edge. Callback function should return the standard topo walker
+ * (TOPO_WALK_*) return values.
+ *
+ * On success, this function returns 0.
+ *
+ * On failure this function returns -1.
+ */
+int
+topo_edge_iter(topo_hdl_t *thp, topo_vertex_t *vtx,
+ int (*func)(topo_hdl_t *, topo_edge_t *, boolean_t, void *), void *arg)
+{
+ uint_t n = 0;
+
+ for (topo_edge_t *edge = topo_list_next(&vtx->tvt_outgoing);
+ edge != NULL; edge = topo_list_next(edge), n++) {
+ int ret;
+ boolean_t last_edge = B_FALSE;
+
+ if (n == (vtx->tvt_noutgoing - 1))
+ last_edge = B_TRUE;
+
+ ret = func(thp, edge, last_edge, arg);
+
+ switch (ret) {
+ case TOPO_WALK_NEXT:
+ continue;
+ case TOPO_WALK_TERMINATE:
+ break;
+ case TOPO_WALK_ERR:
+ default:
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Convenience interface for deallocating a topo_path_t
+ * This function is a NOP if NULL is passed in.
+ */
+void
+topo_path_destroy(topo_hdl_t *thp, topo_path_t *path)
+{
+ topo_path_component_t *pathcomp;
+
+ if (path == NULL)
+ return;
+
+ topo_hdl_strfree(thp, (char *)path->tsp_fmristr);
+ nvlist_free(path->tsp_fmri);
+
+ pathcomp = topo_list_next(&path->tsp_components);
+ while (pathcomp != NULL) {
+ topo_path_component_t *tmp = pathcomp;
+
+ pathcomp = topo_list_next(pathcomp);
+ topo_hdl_free(thp, tmp, sizeof (topo_path_component_t));
+ }
+
+ topo_hdl_free(thp, path, sizeof (topo_path_t));
+}
+
+/*
+ * This just wraps topo_path_t so that visit_vertex() can build a linked list
+ * of paths.
+ */
+struct digraph_path {
+ topo_list_t dgp_link;
+ topo_path_t *dgp_path;
+};
+
+/*
+ * This is a callback function for the vertex iteration that gets initiated by
+ * topo_digraph_paths().
+ *
+ * This is used to implement a depth-first search for all paths that lead to
+ * the vertex pointed to by the "to" parameter. As we walk the graph we
+ * maintain a linked list of the components (vertices) in the in-progress path
+ * as well as the string form of the current path being walked. Whenever we
+ * eoncounter the "to" vertex, we save the current path to the all_paths list
+ * (which keeps track of all the paths we've found to the "to" vertex) and
+ * increment npaths (which keeps track of the number of paths we've found to
+ * the "to" vertex).
+ */
+static int
+visit_vertex(topo_hdl_t *thp, topo_vertex_t *vtx, topo_vertex_t *to,
+ topo_list_t *all_paths, const char *curr_path,
+ topo_list_t *curr_path_comps, uint_t *npaths)
+{
+ struct digraph_path *pathnode = NULL;
+ topo_path_t *path = NULL;
+ topo_path_component_t *pathcomp = NULL;
+ nvlist_t *fmri = NULL;
+ char *pathstr;
+ int err;
+
+ if (asprintf(&pathstr, "%s/%s=%" PRIx64"",
+ curr_path,
+ topo_node_name(vtx->tvt_node),
+ topo_node_instance(vtx->tvt_node)) < 0) {
+ return (topo_hdl_seterrno(thp, ETOPO_NOMEM));
+ }
+
+ /*
+ * Check if this vertex is in the list of vertices in the
+ * curr_path_comps list. If it is, then we've encountered a cycle
+ * and need to turn back.
+ */
+ for (topo_path_component_t *pc = topo_list_next(curr_path_comps);
+ pc != NULL; pc = topo_list_next(pc)) {
+ if (pc->tspc_vertex == vtx) {
+ topo_dprintf(thp, TOPO_DBG_WALK, "Cycle detected: %s",
+ pathstr);
+ free(pathstr);
+ return (0);
+ }
+ }
+
+ if ((pathcomp = topo_hdl_zalloc(thp, sizeof (topo_path_component_t)))
+ == NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ goto err;
+ }
+ pathcomp->tspc_vertex = vtx;
+ topo_list_append(curr_path_comps, pathcomp);
+
+ if (vtx == to) {
+ (*npaths)++;
+ pathnode = topo_hdl_zalloc(thp, sizeof (struct digraph_path));
+
+ if ((path = topo_hdl_zalloc(thp, sizeof (topo_path_t))) ==
+ NULL ||
+ (path->tsp_fmristr = topo_hdl_strdup(thp, pathstr)) ==
+ NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ goto err;
+ }
+
+ if (topo_list_deepcopy(thp, &path->tsp_components,
+ curr_path_comps, sizeof (topo_path_component_t)) != 0) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ }
+ if (topo_fmri_str2nvl(thp, pathstr, &fmri, &err) != 0) {
+ /* errno set */
+ goto err;
+ }
+ path->tsp_fmri = fmri;
+ pathnode->dgp_path = path;
+
+ topo_list_append(all_paths, pathnode);
+ free(pathstr);
+ topo_list_delete(curr_path_comps, pathcomp);
+ topo_hdl_free(thp, pathcomp, sizeof (topo_path_component_t));
+ return (0);
+ }
+
+ for (topo_edge_t *edge = topo_list_next(&vtx->tvt_outgoing);
+ edge != NULL; edge = topo_list_next(edge)) {
+
+ if (visit_vertex(thp, edge->tve_vertex, to, all_paths, pathstr,
+ curr_path_comps, npaths) != 0)
+ goto err;
+ }
+
+ free(pathstr);
+ topo_list_delete(curr_path_comps, pathcomp);
+ topo_hdl_free(thp, pathcomp, sizeof (topo_path_component_t));
+ return (0);
+
+err:
+ free(pathstr);
+ topo_hdl_free(thp, pathnode, sizeof (struct digraph_path));
+ topo_path_destroy(thp, path);
+ return (-1);
+}
+
+/*
+ * On success, 0 is returns and the "paths" parameter is populated with an
+ * array of topo_path_t structs representing all paths from the "from" vertex
+ * to the "to" vertex. The caller is responsible for freeing this array. The
+ * "npaths" parameter will be populated with the number of paths found or 0 if
+ * no paths were found.
+ *
+ * On error, -1 is returned.
+ */
+int
+topo_digraph_paths(topo_hdl_t *thp, topo_digraph_t *tdg, topo_vertex_t *from,
+ topo_vertex_t *to, topo_path_t ***paths, uint_t *npaths)
+{
+ topo_list_t all_paths = { 0 };
+ char *curr_path;
+ topo_path_component_t *pathcomp = NULL;
+ topo_list_t curr_path_comps = { 0 };
+ struct digraph_path *path;
+ uint_t i;
+ int ret;
+
+ if (asprintf(&curr_path, "%s://%s=%s/%s=%" PRIx64"",
+ FM_FMRI_SCHEME_PATH, FM_FMRI_SCHEME, tdg->tdg_scheme,
+ topo_node_name(from->tvt_node),
+ topo_node_instance(from->tvt_node)) < 1) {
+ return (topo_hdl_seterrno(thp, ETOPO_NOMEM));
+ }
+
+ if ((pathcomp = topo_hdl_zalloc(thp, sizeof (topo_path_component_t)))
+ == NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ goto err;
+ }
+ pathcomp->tspc_vertex = from;
+ topo_list_append(&curr_path_comps, pathcomp);
+
+ *npaths = 0;
+ for (topo_edge_t *edge = topo_list_next(&from->tvt_outgoing);
+ edge != NULL; edge = topo_list_next(edge)) {
+
+ ret = visit_vertex(thp, edge->tve_vertex, to, &all_paths,
+ curr_path, &curr_path_comps, npaths);
+ if (ret != 0) {
+ /* errno set */
+ goto err;
+ }
+ }
+ topo_hdl_free(thp, pathcomp, sizeof (topo_path_component_t));
+
+ /*
+ * No paths were found between the "from" and "to" vertices, so
+ * we're done here.
+ */
+ if (*npaths == 0) {
+ free(curr_path);
+ return (0);
+ }
+
+ *paths = topo_hdl_zalloc(thp, (*npaths) * sizeof (topo_path_t *));
+ if (*paths == NULL) {
+ (void) topo_hdl_seterrno(thp, ETOPO_NOMEM);
+ goto err;
+ }
+
+ for (i = 0, path = topo_list_next(&all_paths); path != NULL;
+ i++, path = topo_list_next(path)) {
+
+ *((*paths) + i) = path->dgp_path;
+ }
+
+ path = topo_list_next(&all_paths);
+ while (path != NULL) {
+ struct digraph_path *tmp = path;
+
+ path = topo_list_next(path);
+ topo_hdl_free(thp, tmp, sizeof (struct digraph_path));
+ }
+ free(curr_path);
+ return (0);
+
+err:
+ free(curr_path);
+ path = topo_list_next(&all_paths);
+ while (path != NULL) {
+ struct digraph_path *tmp = path;
+
+ path = topo_list_next(path);
+ topo_hdl_free(thp, tmp, sizeof (struct digraph_path));
+ }
+
+ topo_dprintf(thp, TOPO_DBG_ERR, "%s: failed (%s)", __func__,
+ topo_hdl_errmsg(thp));
+ return (-1);
+}
+
+/* Helper function for path_fmri_nvl2str() */
+static ssize_t
+fmri_bufsz(nvlist_t *nvl)
+{
+ char *dg_scheme = NULL;
+ nvlist_t **hops, *auth;
+ uint_t nhops;
+ ssize_t bufsz = 1;
+ int ret;
+
+ if (nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &auth) != 0 ||
+ nvlist_lookup_string(auth, FM_FMRI_PATH_DIGRAPH_SCHEME,
+ &dg_scheme) != 0) {
+ return (0);
+ }
+
+ if ((ret = snprintf(NULL, 0, "%s://%s=%s", FM_FMRI_SCHEME_PATH,
+ FM_FMRI_SCHEME, dg_scheme)) < 0) {
+ return (-1);
+ }
+ bufsz += ret;
+
+ if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_PATH, &hops, &nhops) !=
+ 0) {
+ return (0);
+ }
+
+ for (uint_t i = 0; i < nhops; i++) {
+ char *name;
+ uint64_t inst;
+
+ if (nvlist_lookup_string(hops[i], FM_FMRI_PATH_NAME, &name) !=
+ 0 ||
+ nvlist_lookup_uint64(hops[i], FM_FMRI_PATH_INST, &inst) !=
+ 0) {
+ return (0);
+ }
+ if ((ret = snprintf(NULL, 0, "/%s=%" PRIx64 "", name, inst)) <
+ 0) {
+ return (-1);
+ }
+ bufsz += ret;
+ }
+ return (bufsz);
+}
+
+int
+path_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ uint8_t scheme_vers;
+ nvlist_t *outnvl;
+ nvlist_t **paths, *auth;
+ uint_t nelem;
+ ssize_t bufsz, end = 0;
+ char *buf, *dg_scheme;
+ int ret;
+
+ if (version > TOPO_METH_NVL2STR_VERSION)
+ return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+ if (nvlist_lookup_uint8(in, FM_FMRI_PATH_VERSION, &scheme_vers) != 0) {
+ return (topo_mod_seterrno(mod, EMOD_NOMEM));
+ }
+
+ if (scheme_vers != FM_PATH_SCHEME_VERSION) {
+ return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+ }
+
+ /*
+ * Get size of buffer needed to hold the string representation of the
+ * FMRI.
+ */
+ bufsz = fmri_bufsz(in);
+ if (bufsz == 0) {
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+ } else if (bufsz < 1) {
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+
+ if ((buf = topo_mod_zalloc(mod, bufsz)) == NULL) {
+ return (topo_mod_seterrno(mod, EMOD_NOMEM));
+ }
+
+ /*
+ * We've already successfully done these nvlist lookups in fmri_bufsz()
+ * so we don't worry about checking retvals this time around.
+ */
+ (void) nvlist_lookup_nvlist(in, FM_FMRI_AUTHORITY, &auth);
+ (void) nvlist_lookup_string(auth, FM_FMRI_PATH_DIGRAPH_SCHEME,
+ &dg_scheme);
+ (void) nvlist_lookup_nvlist_array(in, FM_FMRI_PATH, &paths,
+ &nelem);
+ if ((ret = snprintf(buf, bufsz, "%s://%s=%s", FM_FMRI_SCHEME_PATH,
+ FM_FMRI_SCHEME, dg_scheme)) < 0) {
+ topo_mod_free(mod, buf, bufsz);
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+ end += ret;
+
+ for (uint_t i = 0; i < nelem; i++) {
+ char *pathname;
+ uint64_t pathinst;
+
+ (void) nvlist_lookup_string(paths[i], FM_FMRI_PATH_NAME,
+ &pathname);
+ (void) nvlist_lookup_uint64(paths[i], FM_FMRI_PATH_INST,
+ &pathinst);
+
+ if ((ret = snprintf(buf + end, (bufsz - end), "/%s=%" PRIx64 "",
+ pathname, pathinst)) < 0) {
+ topo_mod_free(mod, buf, bufsz);
+ return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
+ }
+ end += ret;
+ }
+
+ if (topo_mod_nvalloc(mod, &outnvl, NV_UNIQUE_NAME) != 0) {
+ topo_mod_free(mod, buf, bufsz);
+ return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+ }
+ if (nvlist_add_string(outnvl, "fmri-string", buf) != 0) {
+ nvlist_free(outnvl);
+ topo_mod_free(mod, buf, bufsz);
+ return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+ }
+ topo_mod_free(mod, buf, bufsz);
+ *out = outnvl;
+
+ return (0);
+}
+
+int
+path_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ char *fmristr, *tmp = NULL, *lastpair;
+ char *dg_scheme, *dg_scheme_end, *pathname, *path_start;
+ nvlist_t *fmri = NULL, *auth = NULL, **path = NULL;
+ uint_t npairs = 0, i = 0, fmrilen, path_offset;
+
+ if (version > TOPO_METH_STR2NVL_VERSION)
+ return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+ if (nvlist_lookup_string(in, "fmri-string", &fmristr) != 0)
+ return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+
+ if (strncmp(fmristr, "path://", 7) != 0)
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+
+ if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ if (nvlist_add_string(fmri, FM_FMRI_SCHEME,
+ FM_FMRI_SCHEME_PATH) != 0 ||
+ nvlist_add_uint8(fmri, FM_FMRI_PATH_VERSION,
+ FM_PATH_SCHEME_VERSION) != 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+
+ /*
+ * We need to make a copy of the fmri string because strtok will
+ * modify it. We can't use topo_mod_strdup/strfree because
+ * topo_mod_strfree will end up leaking part of the string because
+ * of the NUL chars that strtok inserts - which will cause
+ * topo_mod_strfree to miscalculate the length of the string. So we
+ * keep track of the length of the original string and use
+ * topo_mod_alloc/topo_mod_free.
+ */
+ fmrilen = strlen(fmristr) + 1;
+ if ((tmp = topo_mod_alloc(mod, fmrilen)) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ bcopy(fmristr, tmp, fmrilen);
+
+ /*
+ * Find the offset of the "/" after the authority portion of the FMRI.
+ */
+ if ((path_start = strchr(tmp + 7, '/')) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_FMRI_MALFORM);
+ goto err;
+ }
+
+ path_offset = path_start - tmp;
+ pathname = fmristr + path_offset + 1;
+
+ /*
+ * Count the number of "=" chars after the "path:///" portion of the
+ * FMRI to determine how big the path array needs to be.
+ */
+ (void) strtok_r(tmp + path_offset, "=", &lastpair);
+ while (strtok_r(NULL, "=", &lastpair) != NULL)
+ npairs++;
+
+ if (npairs == 0) {
+ (void) topo_mod_seterrno(mod, EMOD_FMRI_MALFORM);
+ goto err;
+ }
+
+ if ((path = topo_mod_zalloc(mod, npairs * sizeof (nvlist_t *))) ==
+ NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+
+ /*
+ * Build the auth nvlist. There is only one nvpair in the path FMRI
+ * scheme, which is the scheme of the underlying digraph.
+ */
+ if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+
+ if ((dg_scheme = strchr(tmp + 7, '=')) == NULL ||
+ dg_scheme > path_start) {
+ (void) topo_mod_seterrno(mod, EMOD_FMRI_MALFORM);
+ goto err;
+ }
+ dg_scheme_end = tmp + path_offset;
+ *dg_scheme_end = '\0';
+
+ if (nvlist_add_string(auth, FM_FMRI_PATH_DIGRAPH_SCHEME, dg_scheme) !=
+ 0 ||
+ nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, auth) != 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+
+ while (i < npairs) {
+ nvlist_t *pathcomp;
+ uint64_t pathinst;
+ char *end, *addrstr, *estr;
+
+ if (topo_mod_nvalloc(mod, &pathcomp, NV_UNIQUE_NAME) != 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ if ((end = strchr(pathname, '=')) == NULL) {
+ (void) topo_mod_seterrno(mod, EMOD_FMRI_MALFORM);
+ goto err;
+ }
+ *end = '\0';
+ addrstr = end + 1;
+
+ /*
+ * If this is the last pair, then addrstr will already be
+ * nul-terminated.
+ */
+ if (i < (npairs - 1)) {
+ if ((end = strchr(addrstr, '/')) == NULL) {
+ (void) topo_mod_seterrno(mod,
+ EMOD_FMRI_MALFORM);
+ goto err;
+ }
+ *end = '\0';
+ }
+
+ /*
+ * Convert addrstr to a uint64_t
+ */
+ errno = 0;
+ pathinst = strtoull(addrstr, &estr, 16);
+ if (errno != 0 || *estr != '\0') {
+ (void) topo_mod_seterrno(mod, EMOD_FMRI_MALFORM);
+ goto err;
+ }
+
+ /*
+ * Add both nvpairs to the nvlist and then add the nvlist to
+ * the path nvlist array.
+ */
+ if (nvlist_add_string(pathcomp, FM_FMRI_PATH_NAME, pathname) !=
+ 0 ||
+ nvlist_add_uint64(pathcomp, FM_FMRI_PATH_INST, pathinst) !=
+ 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ path[i++] = pathcomp;
+ pathname = end + 1;
+ }
+ if (nvlist_add_nvlist_array(fmri, FM_FMRI_PATH, path, npairs) != 0) {
+ (void) topo_mod_seterrno(mod, EMOD_NOMEM);
+ goto err;
+ }
+ *out = fmri;
+
+ if (path != NULL) {
+ for (i = 0; i < npairs; i++)
+ nvlist_free(path[i]);
+
+ topo_mod_free(mod, path, npairs * sizeof (nvlist_t *));
+ }
+ nvlist_free(auth);
+ topo_mod_free(mod, tmp, fmrilen);
+ return (0);
+
+err:
+ topo_mod_dprintf(mod, "%s failed: %s", __func__,
+ topo_strerror(topo_mod_errno(mod)));
+ if (path != NULL) {
+ for (i = 0; i < npairs; i++)
+ nvlist_free(path[i]);
+
+ topo_mod_free(mod, path, npairs * sizeof (nvlist_t *));
+ }
+ nvlist_free(auth);
+ nvlist_free(fmri);
+ if (tmp != NULL)
+ topo_mod_free(mod, tmp, fmrilen);
+ return (-1);
+}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_digraph.h b/usr/src/lib/fm/topo/libtopo/common/topo_digraph.h
new file mode 100644
index 0000000000..91e9d6139e
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_digraph.h
@@ -0,0 +1,64 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+#ifndef _TOPO_DIGRAPH_H
+#define _TOPO_DIGRAPH_H
+
+#include <fm/topo_mod.h>
+
+#include <topo_list.h>
+#include <topo_prop.h>
+#include <topo_method.h>
+#include <topo_alloc.h>
+#include <topo_error.h>
+#include <topo_file.h>
+#include <topo_module.h>
+#include <topo_string.h>
+#include <topo_subr.h>
+#include <topo_tree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct topo_digraph {
+ topo_list_t tdg_list; /* next/prev pointers */
+ const char *tdg_scheme; /* FMRI scheme */
+ topo_mod_t *tdg_mod; /* builtin enumerator mod */
+ tnode_t *tdg_rootnode; /* see topo_digraph_new() */
+ topo_list_t tdg_vertices; /* adjacency list */
+ uint_t tdg_nvertices; /* total num of vertices */
+ uint_t tdg_nedges; /* total num of edges */
+};
+
+struct topo_vertex {
+ topo_list_t tvt_list; /* next/prev pointers */
+ tnode_t *tvt_node;
+ topo_list_t tvt_incoming;
+ topo_list_t tvt_outgoing;
+ uint_t tvt_nincoming; /* total num incoming edges */
+ uint_t tvt_noutgoing; /* total num outgoing edges */
+};
+
+struct topo_edge {
+ topo_list_t tve_list; /* next/prev pointers */
+ topo_vertex_t *tve_vertex;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOPO_DIGRAPH_H */
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.c b/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.c
new file mode 100644
index 0000000000..52ad4292fc
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.c
@@ -0,0 +1,1502 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+/*
+ * This file implements the following two routines for serializing and
+ * deserializing digraphs to/from XML, respectively:
+ *
+ * topo_digraph_serialize()
+ * topo_digraph_deserialize()
+ *
+ * Refer to the following file for the XML schema being used:
+ * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
+ */
+#include <time.h>
+#include <sys/utsname.h>
+#include <libxml/parser.h>
+#include <libtopo.h>
+
+#include <topo_digraph.h>
+#include <topo_digraph_xml.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+extern int xmlattr_to_int(topo_mod_t *, xmlNodePtr, const char *, uint64_t *);
+static int serialize_nvpair(topo_hdl_t *thp, FILE *, uint_t, const char *,
+ nvpair_t *);
+
+static void
+tdg_xml_nvstring(FILE *fp, uint_t pad, const char *name, const char *value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%s' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_STRING,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvlist(FILE *fp, uint_t pad, const char *name)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_NVLIST);
+}
+
+static void
+tdg_xml_nvuint8(FILE *fp, uint_t pad, const char *name, const uint8_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT8,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvint8(FILE *fp, uint_t pad, const char *name, const uint8_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT8,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvuint16(FILE *fp, uint_t pad, const char *name, const uint8_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT16,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvint16(FILE *fp, uint_t pad, const char *name, const uint8_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_INT16,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvuint32(FILE *fp, uint_t pad, const char *name, const uint32_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%u' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvint32(FILE *fp, uint_t pad, const char *name, const int32_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%d' />\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT32,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvuint64(FILE *fp, uint_t pad, const char *name, const uint64_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='0x%" PRIx64 "' />\n",
+ pad, "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_UINT64, TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvint64(FILE *fp, uint_t pad, const char *name, const int64_t value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%" PRIi64 "' />\n", pad,
+ "", TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_UINT64, TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvdbl(FILE *fp, uint_t pad, const char *name, const double value)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s' %s='%lf' />\n", pad, ""
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, TDG_XML_UINT64,
+ TDG_XML_VALUE, value);
+}
+
+static void
+tdg_xml_nvarray(FILE *fp, uint_t pad, const char *name, const char *type)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE, type);
+}
+
+static void
+tdg_xml_nvint32arr(FILE *fp, uint_t pad, const char *name, int32_t *val,
+ uint_t nelems)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_INT32_ARR);
+
+ for (uint_t i = 0; i < nelems; i++) {
+ (void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "",
+ TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
+}
+
+static void
+tdg_xml_nvuint32arr(FILE *fp, uint_t pad, const char *name, uint32_t *val,
+ uint_t nelems)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_UINT32_ARR);
+
+ for (uint_t i = 0; i < nelems; i++) {
+ (void) fprintf(fp, "%*s<%s %s='%d' />\n", (pad + 2), "",
+ TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
+}
+
+static void
+tdg_xml_nvint64arr(FILE *fp, uint_t pad, const char *name, int64_t *val,
+ uint_t nelems)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_INT64_ARR);
+
+ for (uint_t i = 0; i < nelems; i++) {
+ (void) fprintf(fp, "%*s<%s %s='%" PRIi64 "' />\n", (pad + 2),
+ "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
+}
+
+static void
+tdg_xml_nvuint64arr(FILE *fp, uint_t pad, const char *name, uint64_t *val,
+ uint_t nelems)
+{
+ (void) fprintf(fp, "%*s<%s %s='%s' %s='%s'>\n", pad, "",
+ TDG_XML_NVPAIR, TDG_XML_NAME, name, TDG_XML_TYPE,
+ TDG_XML_UINT64_ARR);
+
+ for (uint_t i = 0; i < nelems; i++) {
+ (void) fprintf(fp, "%*s<%s %s='0x%" PRIx64 "' />\n", (pad + 2),
+ "", TDG_XML_NVPAIR, TDG_XML_VALUE, val[i]);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVPAIR);
+}
+
+static int
+serialize_nvpair_nvlist(topo_hdl_t *thp, FILE *fp, uint_t pad,
+ const char *name, nvlist_t *nvl)
+{
+ nvpair_t *elem = NULL;
+
+ tdg_xml_nvlist(fp, pad, name);
+
+ (void) fprintf(fp, "%*s<%s>\n", pad, "", TDG_XML_NVLIST);
+
+ while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+ char *nvname = nvpair_name(elem);
+
+ if (serialize_nvpair(thp, fp, (pad + 2), nvname, elem) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ }
+
+ (void) fprintf(fp, "%*s</%s>\n", pad, "", TDG_XML_NVLIST);
+ (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", pad, "", TDG_XML_NVPAIR,
+ name);
+
+ return (0);
+}
+
+static int
+serialize_nvpair(topo_hdl_t *thp, FILE *fp, uint_t pad, const char *pname,
+ nvpair_t *nvp)
+{
+ data_type_t type = nvpair_type(nvp);
+
+ switch (type) {
+ case DATA_TYPE_INT8: {
+ int8_t val;
+
+ if (nvpair_value_int8(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint8(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_UINT8: {
+ uint8_t val;
+
+ if (nvpair_value_uint8(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint8(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_INT16: {
+ int16_t val;
+
+ if (nvpair_value_int16(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint16(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_UINT16: {
+ uint16_t val;
+
+ if (nvpair_value_uint16(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint16(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_INT32: {
+ int32_t val;
+
+ if (nvpair_value_int32(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint32(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_UINT32: {
+ uint32_t val;
+
+ if (nvpair_value_uint32(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint32(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_INT64: {
+ int64_t val;
+
+ if (nvpair_value_int64(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint64(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_UINT64: {
+ uint64_t val;
+
+ if (nvpair_value_uint64(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint64(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_DOUBLE: {
+ double val;
+
+ if (nvpair_value_double(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvdbl(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_STRING: {
+ char *val;
+
+ if (nvpair_value_string(nvp, &val) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvstring(fp, pad, pname, val);
+ break;
+ }
+ case DATA_TYPE_NVLIST: {
+ nvlist_t *nvl;
+
+ if (nvpair_value_nvlist(nvp, &nvl) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ if (serialize_nvpair_nvlist(thp, fp, pad + 2, pname,
+ nvl) != 0) {
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+ }
+ break;
+ }
+ case DATA_TYPE_INT32_ARRAY: {
+ uint_t nelems;
+ int32_t *val;
+
+ if (nvpair_value_int32_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint32arr(fp, pad + 2, pname, val, nelems);
+
+ break;
+ }
+ case DATA_TYPE_UINT32_ARRAY: {
+ uint_t nelems;
+ uint32_t *val;
+
+ if (nvpair_value_uint32_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint32arr(fp, pad + 2, pname, val, nelems);
+
+ break;
+ }
+ case DATA_TYPE_INT64_ARRAY: {
+ uint_t nelems;
+ int64_t *val;
+
+ if (nvpair_value_int64_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvint64arr(fp, pad + 2, pname, val, nelems);
+
+ break;
+ }
+ case DATA_TYPE_UINT64_ARRAY: {
+ uint_t nelems;
+ uint64_t *val;
+
+ if (nvpair_value_uint64_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvuint64arr(fp, pad + 2, pname, val, nelems);
+
+ break;
+ }
+ case DATA_TYPE_STRING_ARRAY: {
+ uint_t nelems;
+ char **val;
+
+ if (nvpair_value_string_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvarray(fp, pad, pname, TDG_XML_STRING_ARR);
+ for (uint_t i = 0; i < nelems; i++) {
+ (void) fprintf(fp, "%*s<%s %s='%s' />\n",
+ (pad + 2), "", TDG_XML_NVPAIR,
+ TDG_XML_VALUE, val[i]);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", (pad + 2), "",
+ TDG_XML_NVPAIR);
+
+ break;
+ }
+ case DATA_TYPE_NVLIST_ARRAY: {
+ uint_t nelems;
+ nvlist_t **val;
+
+ if (nvpair_value_nvlist_array(nvp, &val, &nelems) != 0)
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+
+ tdg_xml_nvarray(fp, pad, pname, TDG_XML_NVLIST_ARR);
+ for (uint_t i = 0; i < nelems; i++) {
+ nvpair_t *elem = NULL;
+
+ (void) fprintf(fp, "%*s<%s>\n", (pad + 2), "",
+ TDG_XML_NVLIST);
+
+ while ((elem = nvlist_next_nvpair(val[i],
+ elem)) != NULL) {
+ char *nvname = nvpair_name(elem);
+
+ if (serialize_nvpair(thp, fp,
+ (pad + 4), nvname, elem) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ }
+
+ (void) fprintf(fp, "%*s</%s>\n", (pad + 2), "",
+ TDG_XML_NVLIST);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", pad, "",
+ TDG_XML_NVPAIR);
+
+ break;
+ }
+ default:
+ topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data "
+ "type: %d\n", type);
+ (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+serialize_edge(topo_hdl_t *thp, topo_edge_t *edge, boolean_t last_edge,
+ void *arg)
+{
+ nvlist_t *fmri = NULL;
+ char *fmristr;
+ int err;
+ tnode_t *tn;
+ FILE *fp = (FILE *)arg;
+
+ tn = topo_vertex_node(edge->tve_vertex);
+ if (topo_node_resource(tn, &fmri, &err) != 0 ||
+ topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
+ /* errno set */
+ nvlist_free(fmri);
+ return (TOPO_WALK_ERR);
+ }
+ nvlist_free(fmri);
+
+ (void) fprintf(fp, "%*s<%s %s='%s' />\n", 4, "", TDG_XML_EDGE,
+ TDG_XML_FMRI, fmristr);
+ topo_hdl_strfree(thp, fmristr);
+
+ return (TOPO_WALK_NEXT);
+}
+
+/*
+ * Some node property values aren't available unless we go through the libtopo
+ * API's topo_prop_get_* routines. We do that here to make sure the nodes have
+ * all of their properties populated, then we vector off to type-specific
+ * XML serialization functions.
+ */
+static int
+serialize_property(topo_hdl_t *thp, FILE *fp, uint_t pad, tnode_t *tn,
+ topo_propval_t *pv, const char *pgname)
+{
+ topo_type_t type = pv->tp_type;
+ const char *pname = pv->tp_name;
+ int err;
+ char *name = TDG_XML_PROP_VALUE;
+
+ switch (type) {
+ case TOPO_TYPE_INT32: {
+ int32_t val;
+
+ if (topo_prop_get_int32(tn, pgname, pname, &val,
+ &err) != 0)
+ return (-1);
+
+ tdg_xml_nvint32(fp, pad, name, val);
+ break;
+ }
+ case TOPO_TYPE_UINT32: {
+ uint32_t val;
+
+ if (topo_prop_get_uint32(tn, pgname, pname, &val,
+ &err) != 0)
+ return (-1);
+
+ tdg_xml_nvuint32(fp, pad, name, val);
+ break;
+ }
+ case TOPO_TYPE_INT64: {
+ int64_t val;
+
+ if (topo_prop_get_int64(tn, pgname, pname, &val,
+ &err) != 0)
+ return (-1);
+
+ tdg_xml_nvint64(fp, pad, name, val);
+ break;
+ }
+ case TOPO_TYPE_UINT64: {
+ uint64_t val;
+
+ if (topo_prop_get_uint64(tn, pgname, pname, &val,
+ &err) != 0)
+ return (-1);
+
+ tdg_xml_nvuint64(fp, pad, name, val);
+ break;
+ }
+ case TOPO_TYPE_STRING: {
+ char *val;
+
+ if (topo_prop_get_string(tn, pgname, pname, &val,
+ &err) != 0)
+ return (-1);
+
+ tdg_xml_nvstring(fp, pad, name, val);
+
+ topo_hdl_strfree(thp, val);
+ break;
+ }
+ case TOPO_TYPE_FMRI: {
+ nvlist_t *nvl;
+
+ if (topo_prop_get_fmri(tn, pgname, pname, &nvl,
+ &err) != 0)
+ return (-1);
+
+ if (serialize_nvpair_nvlist(thp, fp, pad + 2, name,
+ nvl) != 0) {
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ nvlist_free(nvl);
+ break;
+ }
+ case TOPO_TYPE_INT32_ARRAY: {
+ uint_t nelems;
+ int32_t *val;
+
+ if (topo_prop_get_int32_array(tn, pgname, pname, &val,
+ &nelems, &err) != 0)
+ return (-1);
+
+ tdg_xml_nvint32arr(fp, pad, pname, val, nelems);
+ topo_hdl_free(thp, val, (sizeof (int32_t) * nelems));
+ break;
+ }
+ case TOPO_TYPE_UINT32_ARRAY: {
+ uint_t nelems;
+ uint32_t *val;
+
+ if (topo_prop_get_uint32_array(tn, pgname, pname, &val,
+ &nelems, &err) != 0)
+ return (-1);
+
+ tdg_xml_nvuint32arr(fp, pad, pname, val, nelems);
+ topo_hdl_free(thp, val, (sizeof (uint32_t) * nelems));
+ break;
+ }
+ case TOPO_TYPE_INT64_ARRAY: {
+ uint_t nelems;
+ int64_t *val;
+
+ if (topo_prop_get_int64_array(tn, pgname, pname, &val,
+ &nelems, &err) != 0)
+ return (-1);
+
+ tdg_xml_nvint64arr(fp, pad, pname, val, nelems);
+ topo_hdl_free(thp, val, (sizeof (int64_t) * nelems));
+ break;
+ }
+ case TOPO_TYPE_UINT64_ARRAY: {
+ uint_t nelems;
+ uint64_t *val;
+
+ if (topo_prop_get_uint64_array(tn, pgname, pname, &val,
+ &nelems, &err) != 0)
+ return (-1);
+
+ tdg_xml_nvuint64arr(fp, pad, pname, val, nelems);
+ topo_hdl_free(thp, val, (sizeof (uint64_t) * nelems));
+ break;
+ }
+ default:
+ topo_dprintf(thp, TOPO_DBG_XML, "Invalid nvpair data "
+ "type: %d\n", type);
+ (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+serialize_pgroups(topo_hdl_t *thp, FILE *fp, tnode_t *tn)
+{
+ topo_pgroup_t *pg;
+ uint_t npgs = 0;
+
+ for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL;
+ pg = topo_list_next(pg)) {
+
+ npgs++;
+ }
+
+ tdg_xml_nvarray(fp, 2, TDG_XML_PGROUPS, TDG_XML_NVLIST_ARR);
+
+ for (pg = topo_list_next(&tn->tn_pgroups); pg != NULL;
+ pg = topo_list_next(pg)) {
+
+ topo_proplist_t *pvl;
+ uint_t nprops = 0;
+
+ (void) fprintf(fp, "%*s<%s>\n", 4, "", TDG_XML_NVLIST);
+ tdg_xml_nvstring(fp, 6, TOPO_PROP_GROUP_NAME,
+ pg->tpg_info->tpi_name);
+
+ for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
+ pvl = topo_list_next(pvl))
+ nprops++;
+
+ tdg_xml_nvarray(fp, 6, TDG_XML_PVALS, TDG_XML_NVLIST_ARR);
+
+ for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
+ pvl = topo_list_next(pvl)) {
+
+ topo_propval_t *pv = pvl->tp_pval;
+
+ (void) fprintf(fp, "%*s<%s>\n", 8, "", TDG_XML_NVLIST);
+ tdg_xml_nvstring(fp, 10, TDG_XML_PROP_NAME,
+ pv->tp_name);
+ tdg_xml_nvuint32(fp, 10, TDG_XML_PROP_TYPE,
+ pv->tp_type);
+
+ if (serialize_property(thp, fp, 10, tn, pv,
+ pg->tpg_info->tpi_name) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ (void) fprintf(fp, "%*s</%s>\n", 8, "",
+ TDG_XML_NVLIST);
+ }
+
+ (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 6, "",
+ TDG_XML_NVPAIR, TDG_XML_PVALS);
+ (void) fprintf(fp, "%*s</%s>\n", 4, "", TDG_XML_NVLIST);
+ }
+ (void) fprintf(fp, "%*s</%s> <!-- %s -->\n", 2, "", TDG_XML_NVPAIR,
+ TDG_XML_PGROUPS);
+
+ return (0);
+}
+
+static int
+serialize_vertex(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx,
+ void *arg)
+{
+ nvlist_t *fmri = NULL;
+ char *fmristr;
+ tnode_t *tn;
+ int err;
+ FILE *fp = (FILE *)arg;
+
+ tn = topo_vertex_node(vtx);
+ if (topo_node_resource(tn, &fmri, &err) != 0 ||
+ topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
+ /* errno set */
+ nvlist_free(fmri);
+ return (TOPO_WALK_ERR);
+ }
+ nvlist_free(fmri);
+
+ (void) fprintf(fp, "<%s %s='%s' %s='0x%" PRIx64 "' %s='%s'>\n",
+ TDG_XML_VERTEX, TDG_XML_NAME, topo_node_name(tn),
+ TDG_XML_INSTANCE, topo_node_instance(tn),
+ TDG_XML_FMRI, fmristr);
+
+ topo_hdl_strfree(thp, fmristr);
+
+ if (serialize_pgroups(thp, fp, tn) != 0) {
+ /* errno set */
+ return (TOPO_WALK_ERR);
+ }
+
+ if (vtx->tvt_noutgoing != 0) {
+ (void) fprintf(fp, " <%s>\n", TDG_XML_OUTEDGES);
+
+ if (topo_edge_iter(thp, vtx, serialize_edge, fp) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to iterate "
+ "edges on %s=%" PRIx64 "\n", topo_node_name(tn),
+ topo_node_instance(tn));
+ /* errno set */
+ return (TOPO_WALK_ERR);
+ }
+ (void) fprintf(fp, " </%s>\n", TDG_XML_OUTEDGES);
+ }
+ (void) fprintf(fp, "</%s>\n\n", TDG_XML_VERTEX);
+
+ return (TOPO_WALK_NEXT);
+}
+
+/*
+ * This function takes a topo_digraph_t and serializes it to XML.
+ *
+ * The schema is described in detail in:
+ * usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
+ *
+ * On success, this function writes the XML to the specified file and
+ * returns 0.
+ *
+ * On failure, this function returns -1.
+ */
+int
+topo_digraph_serialize(topo_hdl_t *thp, topo_digraph_t *tdg, FILE *fp)
+{
+ struct utsname uts = { 0 };
+ time_t utc_time;
+ char tstamp[64];
+ int ret;
+
+ if ((ret = uname(&uts)) < 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "uname failed (ret = %d)\n",
+ ret);
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+ }
+
+ if (time(&utc_time) < 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "uname failed (%s)\n",
+ strerror(errno));
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+ }
+
+ /*
+ * strftime returns 0 if the size of the result is larger than the
+ * buffer size passed in to it. We've sized tstamp to be pretty
+ * large, so this really shouldn't happen.
+ */
+ if (strftime(tstamp, sizeof (tstamp), "%Y-%m-%dT%H:%M:%SZ",
+ gmtime(&utc_time)) == 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "strftime failed\n");
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+ }
+
+ (void) fprintf(fp, "<?xml version=\"1.0\"?>\n");
+ (void) fprintf(fp, "<!DOCTYPE topology SYSTEM \"%s\">\n", TDG_DTD);
+ (void) fprintf(fp, "<%s %s='%s' %s='%s' %s='%s' %s='%s' %s='%s'>\n",
+ TDG_XML_TOPO_DIGRAPH, TDG_XML_SCHEME, tdg->tdg_scheme,
+ TDG_XML_NODENAME, uts.nodename, TDG_XML_OSVERSION, uts.version,
+ TDG_XML_PRODUCT, thp->th_product, TDG_XML_TSTAMP, tstamp);
+ (void) fprintf(fp, "<%s>\n", TDG_XML_VERTICES);
+
+ if (topo_vertex_iter(thp, tdg, serialize_vertex, fp) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "\nfailed to iterate "
+ "vertices\n");
+ /* errno set */
+ return (-1);
+ }
+
+ (void) fprintf(fp, "</%s>\n", TDG_XML_VERTICES);
+ (void) fprintf(fp, "</%s>\n", TDG_XML_TOPO_DIGRAPH);
+
+ if (ferror(fp) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "An unknown error ocurrred "
+ "while writing out the serialize topology.");
+ return (topo_hdl_seterrno(thp, ETOPO_UNKNOWN));
+ }
+ return (0);
+}
+
+static xmlNodePtr
+get_child_by_name(xmlNodePtr xn, xmlChar *name)
+{
+ for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
+ if (xmlStrcmp(cn->name, name) == 0)
+ return (cn);
+
+ return (NULL);
+}
+
+static void
+dump_xml_node(topo_hdl_t *thp, xmlNodePtr xn)
+{
+ topo_dprintf(thp, TOPO_DBG_XML, "node: %s", (char *)xn->name);
+ for (xmlAttrPtr attr = xn->properties; attr != NULL; attr = attr->next)
+ topo_dprintf(thp, TOPO_DBG_XML, "attribute: %s",
+ (char *)attr->name);
+
+ for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
+ topo_dprintf(thp, TOPO_DBG_XML, "\tchild node: %s",
+ (char *)cn->name);
+}
+
+struct edge_cb_arg {
+ const char *from_fmri;
+ const char *to_fmri;
+ topo_vertex_t *from_vtx;
+ topo_vertex_t *to_vtx;
+};
+
+static int
+edge_cb(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx, void *arg)
+{
+ struct edge_cb_arg *cbarg = arg;
+ tnode_t *tn;
+ nvlist_t *fmri = NULL;
+ char *fmristr = NULL;
+ int err;
+
+ tn = topo_vertex_node(vtx);
+ if (topo_node_resource(tn, &fmri, &err) != 0 ||
+ topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to convert FMRI for "
+ "%s=%" PRIx64 " to a string\n", topo_node_name(tn),
+ topo_node_instance(tn));
+ if (thp->th_debug & TOPO_DBG_XML)
+ nvlist_print(stdout, fmri);
+ nvlist_free(fmri);
+ return (TOPO_WALK_ERR);
+ }
+ nvlist_free(fmri);
+
+ if (strcmp(fmristr, cbarg->from_fmri) == 0)
+ cbarg->from_vtx = vtx;
+ else if (strcmp(fmristr, cbarg->to_fmri) == 0)
+ cbarg->to_vtx = vtx;
+
+ topo_hdl_strfree(thp, fmristr);
+ if (cbarg->from_vtx != NULL && cbarg->to_vtx != NULL)
+ return (TOPO_WALK_TERMINATE);
+ else
+ return (TOPO_WALK_NEXT);
+}
+
+static int
+deserialize_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
+ xmlChar *from_fmri, xmlNodePtr xn)
+{
+ for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL;
+ cn = cn->next) {
+ xmlChar *fmri;
+ struct edge_cb_arg cbarg = { 0 };
+
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_EDGE) != 0)
+ continue;
+
+ if ((fmri = xmlGetProp(cn, (xmlChar *)TDG_XML_FMRI)) == NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML,
+ "error parsing %s element", (char *)cn->name);
+ dump_xml_node(thp, cn);
+ return (-1);
+ }
+ cbarg.from_fmri = (char *)from_fmri;
+ cbarg.to_fmri = (char *)fmri;
+
+ if (topo_vertex_iter(mod->tm_hdl, tdg, edge_cb, &cbarg) != 0) {
+ xmlFree(fmri);
+ return (-1);
+ }
+ xmlFree(fmri);
+
+ if (cbarg.from_vtx == NULL || cbarg.to_vtx == NULL) {
+ return (-1);
+ }
+ if (topo_edge_new(mod, cbarg.from_vtx, cbarg.to_vtx) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+add_edges(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
+ xmlNodePtr xn)
+{
+ int ret = -1;
+ nvlist_t *props = NULL;
+ xmlChar *name = NULL, *fmri = NULL;
+ xmlNodePtr cn;
+ uint64_t inst;
+
+ if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
+ (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL ||
+ xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) {
+ goto fail;
+ }
+
+ if ((cn = get_child_by_name(xn, (xmlChar *)TDG_XML_OUTEDGES)) !=
+ NULL) {
+ if (deserialize_edges(thp, mod, tdg, fmri, cn) != 0)
+ goto fail;
+ }
+ ret = 0;
+
+fail:
+ if (ret != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element",
+ __func__, TDG_XML_VERTEX);
+ dump_xml_node(thp, xn);
+ }
+ nvlist_free(props);
+ if (name != NULL)
+ xmlFree(name);
+ if (fmri != NULL)
+ xmlFree(fmri);
+
+ return (ret);
+}
+
+static topo_pgroup_info_t pginfo = {
+ NULL,
+ TOPO_STABILITY_PRIVATE,
+ TOPO_STABILITY_PRIVATE,
+ 1
+};
+
+static int
+add_props(topo_hdl_t *thp, topo_vertex_t *vtx, nvlist_t *pgroups)
+{
+ tnode_t *tn;
+ nvlist_t **pgs;
+ uint_t npgs = 0;
+
+ tn = topo_vertex_node(vtx);
+ if (nvlist_lookup_nvlist_array(pgroups, TDG_XML_PGROUPS, &pgs,
+ &npgs) != 0) {
+ goto fail;
+ }
+
+ for (uint_t i = 0; i < npgs; i++) {
+ char *pgname;
+ nvlist_t **props;
+ uint_t nprops;
+ int err;
+
+ if (nvlist_lookup_string(pgs[i], TDG_XML_PGROUP_NAME,
+ &pgname) != 0 ||
+ nvlist_lookup_nvlist_array(pgs[i], TDG_XML_PVALS, &props,
+ &nprops) != 0) {
+ goto fail;
+ }
+ pginfo.tpi_name = pgname;
+
+ if (topo_pgroup_create(tn, &pginfo, &err) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to create "
+ "pgroup: %s", pgname);
+ goto fail;
+ }
+ for (uint_t j = 0; j < nprops; j++) {
+ if (topo_prop_setprop(tn, pgname, props[j],
+ TOPO_PROP_IMMUTABLE, props[j], &err) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to "
+ "set properties in pgroup: %s", pgname);
+ goto fail;
+ }
+ }
+ }
+ return (0);
+fail:
+ topo_dprintf(thp, TOPO_DBG_XML, "%s: error decoding properties for "
+ "%s=%" PRIx64, __func__, topo_node_name(tn),
+ topo_node_instance(tn));
+ if (thp->th_debug & TOPO_DBG_XML)
+ nvlist_print(stdout, pgroups);
+
+ return (-1);
+}
+
+static void
+free_nvlist_array(topo_hdl_t *thp, nvlist_t **nvlarr, uint_t nelems)
+{
+ for (uint_t i = 0; i < nelems; i++) {
+ if (nvlarr[i] != NULL)
+ nvlist_free(nvlarr[i]);
+ }
+ topo_hdl_free(thp, nvlarr, nelems * sizeof (nvlist_t *));
+}
+
+static boolean_t
+is_overflow(topo_hdl_t *thp, uint64_t val, uint_t nbits)
+{
+ if ((val >> nbits) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "value exceeds %u bits", nbits);
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Recursive function for parsing nvpair XML elements, which can contain
+ * nested nvlist and nvpair elements.
+ */
+static int
+deserialize_nvpair(topo_hdl_t *thp, topo_mod_t *mod, nvlist_t *nvl,
+ xmlNodePtr xn)
+{
+ int ret = -1;
+ xmlChar *name = NULL, *type = NULL, *sval = NULL;
+ uint64_t val;
+
+ if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
+ (type = xmlGetProp(xn, (xmlChar *)TDG_XML_TYPE)) == NULL) {
+ goto fail;
+ }
+
+ if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST) == 0) {
+ nvlist_t *cnvl = NULL;
+
+ if (topo_hdl_nvalloc(thp, &cnvl, NV_UNIQUE_NAME) != 0) {
+ goto fail;
+ }
+
+ for (xmlNodePtr cn = xn->xmlChildrenNode;
+ cn != NULL; cn = cn->next) {
+
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) != 0)
+ continue;
+
+ for (xmlNodePtr gcn = cn->xmlChildrenNode;
+ gcn != NULL; gcn = gcn->next) {
+
+ if (xmlStrcmp(gcn->name,
+ (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+ if (deserialize_nvpair(thp, mod, cnvl, gcn) !=
+ 0) {
+ nvlist_free(cnvl);
+ goto fail;
+ }
+ }
+ if (nvlist_add_nvlist(nvl, (char *)name, cnvl) != 0) {
+ nvlist_free(cnvl);
+ goto fail;
+ }
+ nvlist_free(cnvl);
+ break;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT8) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 8) ||
+ nvlist_add_int8(nvl, (char *)name, (int8_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT16) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 16) ||
+ nvlist_add_int16(nvl, (char *)name, (int16_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 32) ||
+ nvlist_add_int32(nvl, (char *)name, (int32_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ nvlist_add_int64(nvl, (char *)name, (int64_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT8) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 8) ||
+ nvlist_add_uint8(nvl, (char *)name, (uint8_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT16) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 16) ||
+ nvlist_add_uint16(nvl, (char *)name, (uint16_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ is_overflow(thp, val, 32) ||
+ nvlist_add_uint32(nvl, (char *)name, (uint32_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64) == 0) {
+ if (xmlattr_to_int(mod, xn, TDG_XML_VALUE, &val) != 0 ||
+ nvlist_add_uint64(nvl, (char *)name, (uint64_t)val) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_STRING) == 0) {
+ if ((sval = xmlGetProp(xn, (xmlChar *)TDG_XML_VALUE)) == NULL ||
+ nvlist_add_string(nvl, (char *)name, (char *)sval) != 0) {
+ goto fail;
+ }
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_NVLIST_ARR) == 0) {
+ uint64_t nelem = 0;
+ nvlist_t **nvlarr = NULL;
+ uint_t i = 0;
+ xmlNodePtr cn = xn->xmlChildrenNode;
+
+ /*
+ * Count the number of child nvlist elements
+ */
+ while (cn != NULL) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) ==
+ 0) {
+ nelem++;
+ }
+ cn = cn->next;
+ }
+
+ if ((nvlarr = topo_hdl_zalloc(thp,
+ (nelem * sizeof (nvlist_t *)))) == NULL) {
+ goto fail;
+ }
+
+ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVLIST) !=
+ 0)
+ continue;
+
+ if (topo_hdl_nvalloc(thp, &nvlarr[i],
+ NV_UNIQUE_NAME) != 0) {
+ free_nvlist_array(thp, nvlarr, nelem);
+ goto fail;
+ }
+
+ for (xmlNodePtr gcn = cn->xmlChildrenNode;
+ gcn != NULL; gcn = gcn->next) {
+ if (xmlStrcmp(gcn->name,
+ (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+ if (deserialize_nvpair(thp, mod, nvlarr[i],
+ gcn) != 0) {
+ free_nvlist_array(thp, nvlarr, nelem);
+ goto fail;
+ }
+ }
+ i++;
+ }
+ if (nvlist_add_nvlist_array(nvl, (char *)name, nvlarr,
+ nelem) != 0) {
+ free_nvlist_array(thp, nvlarr, nelem);
+ goto fail;
+ }
+ free_nvlist_array(thp, nvlarr, nelem);
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT32_ARR) == 0) {
+ uint64_t nelem = 0;
+ uint32_t *arr = NULL;
+ uint_t i = 0;
+ xmlNodePtr cn = xn->xmlChildrenNode;
+
+ /*
+ * Count the number of child nvpair elements
+ */
+ while (cn != NULL) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
+ 0) {
+ nelem++;
+ }
+ cn = cn->next;
+ }
+
+ if ((arr = topo_hdl_zalloc(thp,
+ (nelem * sizeof (uint32_t)))) == NULL) {
+ goto fail;
+ }
+
+ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+
+ if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
+ topo_hdl_free(thp, arr,
+ (nelem * sizeof (uint32_t)));
+ goto fail;
+ }
+
+ arr[i] = val;
+ i++;
+ }
+ if (nvlist_add_uint32_array(nvl, (char *)name, arr,
+ nelem) != 0) {
+ topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t)));
+ goto fail;
+ }
+ topo_hdl_free(thp, arr, (nelem * sizeof (uint32_t)));
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT32_ARR) == 0) {
+ uint64_t nelem = 0;
+ int32_t *arr = NULL;
+ uint_t i = 0;
+ xmlNodePtr cn = xn->xmlChildrenNode;
+
+ /*
+ * Count the number of child nvpair elements
+ */
+ while (cn != NULL) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
+ 0) {
+ nelem++;
+ }
+ cn = cn->next;
+ }
+
+ if ((arr = topo_hdl_zalloc(thp,
+ (nelem * sizeof (int32_t)))) == NULL) {
+ goto fail;
+ }
+
+ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+
+ if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
+ topo_hdl_free(thp, arr,
+ (nelem * sizeof (int32_t)));
+ goto fail;
+ }
+
+ arr[i] = val;
+ i++;
+ }
+ if (nvlist_add_int32_array(nvl, (char *)name, arr,
+ nelem) != 0) {
+ topo_hdl_free(thp, arr, (nelem * sizeof (int32_t)));
+ goto fail;
+ }
+ topo_hdl_free(thp, arr, (nelem * sizeof (int32_t)));
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_UINT64_ARR) == 0) {
+ uint64_t nelem = 0, *arr = NULL;
+ uint_t i = 0;
+ xmlNodePtr cn = xn->xmlChildrenNode;
+
+ /*
+ * Count the number of child nvpair elements
+ */
+ while (cn != NULL) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
+ 0) {
+ nelem++;
+ }
+ cn = cn->next;
+ }
+
+ if ((arr = topo_hdl_zalloc(thp,
+ (nelem * sizeof (uint64_t)))) == NULL) {
+ goto fail;
+ }
+
+ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+
+ if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
+ topo_hdl_free(thp, arr,
+ (nelem * sizeof (uint64_t)));
+ goto fail;
+ }
+
+ arr[i] = val;
+ i++;
+ }
+ if (nvlist_add_uint64_array(nvl, (char *)name, arr,
+ nelem) != 0) {
+ topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t)));
+ goto fail;
+ }
+ topo_hdl_free(thp, arr, (nelem * sizeof (uint64_t)));
+ } else if (xmlStrcmp(type, (xmlChar *)TDG_XML_INT64_ARR) == 0) {
+ uint64_t nelem = 0;
+ int64_t *arr = NULL;
+ uint_t i = 0;
+ xmlNodePtr cn = xn->xmlChildrenNode;
+
+ /*
+ * Count the number of child nvpair elements
+ */
+ while (cn != NULL) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) ==
+ 0) {
+ nelem++;
+ }
+ cn = cn->next;
+ }
+
+ if ((arr = topo_hdl_zalloc(thp,
+ (nelem * sizeof (int64_t)))) == NULL) {
+ goto fail;
+ }
+
+ for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) != 0)
+ continue;
+
+ if (xmlattr_to_int(mod, cn, TDG_XML_VALUE, &val) != 0) {
+ topo_hdl_free(thp, arr,
+ (nelem * sizeof (int64_t)));
+ goto fail;
+ }
+
+ arr[i] = val;
+ i++;
+ }
+ if (nvlist_add_int64_array(nvl, (char *)name, arr,
+ nelem) != 0) {
+ topo_hdl_free(thp, arr, (nelem * sizeof (int64_t)));
+ goto fail;
+ }
+ topo_hdl_free(thp, arr, (nelem * sizeof (int64_t)));
+ }
+ ret = 0;
+fail:
+ if (ret != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s "
+ "element: name: %s, type: %s, nvl: %p", __func__, xn->name,
+ (name != NULL) ? (char *)name : "MISSING!",
+ (type != NULL) ? (char *)type : "MISSING!", nvl);
+ dump_xml_node(thp, xn);
+ }
+ if (name != NULL)
+ xmlFree(name);
+ if (type != NULL)
+ xmlFree(type);
+ if (sval != NULL)
+ xmlFree(sval);
+
+ return (ret);
+}
+
+static int
+deserialize_vertex(topo_hdl_t *thp, topo_mod_t *mod, topo_digraph_t *tdg,
+ xmlNodePtr xn)
+{
+ int ret = -1;
+ topo_vertex_t *vtx = NULL;
+ nvlist_t *props = NULL;
+ xmlChar *name = NULL, *fmri = NULL;
+ uint64_t inst;
+
+ if ((name = xmlGetProp(xn, (xmlChar *)TDG_XML_NAME)) == NULL ||
+ (fmri = xmlGetProp(xn, (xmlChar *)TDG_XML_FMRI)) == NULL ||
+ xmlattr_to_int(mod, xn, TDG_XML_INSTANCE, &inst) != 0) {
+ goto fail;
+ }
+
+ if ((vtx = topo_vertex_new(mod, (char *)name, inst)) == NULL) {
+ goto fail;
+ }
+
+ for (xmlNodePtr cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
+ if (xmlStrcmp(cn->name, (xmlChar *)TDG_XML_NVPAIR) == 0) {
+ if (topo_hdl_nvalloc(thp, &props, NV_UNIQUE_NAME) != 0)
+ goto fail;
+ if (deserialize_nvpair(thp, mod, props, cn) != 0 ||
+ add_props(thp, vtx, props) != 0)
+ goto fail;
+ }
+ }
+ ret = 0;
+
+fail:
+ if (ret != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "%s: error parsing %s element",
+ __func__, TDG_XML_VERTEX);
+ dump_xml_node(thp, xn);
+ }
+ nvlist_free(props);
+ if (name != NULL)
+ xmlFree(name);
+ if (fmri != NULL)
+ xmlFree(fmri);
+
+ return (ret);
+}
+
+/*
+ * This function takes a buffer containing XML data describing a directed graph
+ * topology. This data is parsed to the original directed graph is rehydrated.
+ *
+ * On success, a pointer to a topo_digraph_t representing the graph is
+ * returned. The caller is responsible for destroying the graph via a call to
+ * topo_digraph_destroy()
+ *
+ * On failure, NULL is returned.
+ */
+topo_digraph_t *
+topo_digraph_deserialize(topo_hdl_t *thp, const char *xml, size_t sz)
+{
+ xmlDocPtr doc;
+ xmlDtdPtr dtd = NULL;
+ xmlNodePtr root, vertices;
+ xmlChar *scheme = NULL;
+ topo_mod_t *mod;
+ topo_digraph_t *tdg, *ret = NULL;
+
+ if ((doc = xmlReadMemory(xml, sz, "", NULL, 0)) == NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML, "Failed to parse XML");
+ goto fail;
+ }
+
+ /*
+ * As a sanity check, extract the DTD from the XML and verify it
+ * matches the DTD for a digraph topology.
+ */
+ if ((dtd = xmlGetIntSubset(doc)) == NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML, "document has no DTD.\n");
+ goto fail;
+ }
+
+ if (strcmp((const char *)dtd->SystemID, TDG_DTD) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "unexpected DTD: %s",
+ dtd->SystemID);
+ goto fail;
+ }
+
+ /*
+ * Verify the root element is what we're expecting and then grab the
+ * FMRI scheme from its attributes.
+ */
+ if ((root = xmlDocGetRootElement(doc)) == NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML, "document is empty.\n");
+ goto fail;
+ }
+
+ if (xmlStrcmp(root->name, (xmlChar *)TDG_XML_TOPO_DIGRAPH) != 0 ||
+ (scheme = xmlGetProp(root, (xmlChar *)TDG_XML_SCHEME)) ==
+ NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML,
+ "failed to parse %s element", TDG_XML_TOPO_DIGRAPH);
+ goto fail;
+ }
+
+ /*
+ * Load the topo module associated with this FMRI scheme.
+ */
+ if ((mod = topo_mod_lookup(thp, (const char *)scheme, 1)) == NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to load %s module",
+ scheme);
+ goto fail;
+ }
+ /*
+ * If we have a builtin module for this scheme, then there will
+ * already be an empty digraph attached to the handle. Otherwise,
+ * create a new empty digraph and attach it to the handle.
+ */
+ tdg = topo_digraph_get(mod->tm_hdl, mod->tm_info->tmi_scheme);
+ if (tdg == NULL) {
+ if ((tdg = topo_digraph_new(thp, mod, (const char *)scheme)) ==
+ NULL) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to create new "
+ "digraph");
+ goto fail;
+ } else {
+ topo_list_append(&thp->th_digraphs, tdg);
+ }
+ }
+
+ /*
+ * Iterate through the vertex XML elements to reconstruct the graph
+ */
+ vertices = get_child_by_name(root, (xmlChar *)TDG_XML_VERTICES);
+ if (vertices == NULL ||
+ xmlStrcmp(vertices->name, (xmlChar *)TDG_XML_VERTICES) != 0) {
+ topo_dprintf(thp, TOPO_DBG_XML, "failed to parse %s element",
+ TDG_XML_VERTICES);
+ dump_xml_node(thp, root);
+ goto fail;
+ }
+
+ for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL;
+ xn = xn->next) {
+ if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0)
+ continue;
+ if (deserialize_vertex(thp, mod, tdg, xn) != 0)
+ goto fail;
+ }
+
+ /*
+ * Now that all of the vertices have been created, go back through
+ * the vertex XML elements and add the edges.
+ */
+ for (xmlNodePtr xn = vertices->xmlChildrenNode; xn != NULL;
+ xn = xn->next) {
+ if (xmlStrcmp(xn->name, (xmlChar *)TDG_XML_VERTEX) != 0)
+ continue;
+ if (add_edges(thp, mod, tdg, xn) != 0)
+ goto fail;
+ }
+
+ ret = tdg;
+
+fail:
+ if (scheme != NULL)
+ xmlFree(scheme);
+
+ if (doc != NULL)
+ xmlFreeDoc(doc);
+
+ (void) topo_hdl_seterrno(thp, ETOPO_MOD_XENUM);
+ return (ret);
+}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.h b/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.h
new file mode 100644
index 0000000000..d52d6b8867
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_digraph_xml.h
@@ -0,0 +1,88 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+#ifndef _TOPO_DIGRAPH_XML_H
+#define _TOPO_DIGRAPH_XML_H
+
+#include <fm/topo_mod.h>
+
+#include <topo_list.h>
+#include <topo_prop.h>
+#include <topo_method.h>
+#include <topo_alloc.h>
+#include <topo_error.h>
+#include <topo_module.h>
+#include <topo_string.h>
+#include <topo_subr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TDG_DTD "/usr/share/lib/xml/dtd/digraph-topology.dtd.1"
+
+/*
+ * List of attribute names and values used when serializing a topo_digraph_t
+ * to XML.
+ *
+ * When deserializing an XML representation of a topo_digraph_t, the XML is
+ * first converted to an nvlist representation and then that nvlist is
+ * processed to produce a topo_digraph_t. These property names are also
+ * used as the nvpair names in that intermediate nvlist.
+ */
+#define TDG_XML_EDGE "edge"
+#define TDG_XML_FMRI "fmri"
+#define TDG_XML_SCHEME "fmri-scheme"
+#define TDG_XML_NAME "name"
+#define TDG_XML_NVLIST "nvlist"
+#define TDG_XML_NVLIST_ARR "nvlist-array"
+#define TDG_XML_NVPAIR "nvpair"
+#define TDG_XML_INSTANCE "instance"
+#define TDG_XML_INT8 "int8"
+#define TDG_XML_INT16 "int16"
+#define TDG_XML_INT32 "int32"
+#define TDG_XML_INT32_ARR "int32-array"
+#define TDG_XML_INT64 "int64"
+#define TDG_XML_INT64_ARR "int64-array"
+#define TDG_XML_OSVERSION "os-version"
+#define TDG_XML_NODENAME "nodename"
+#define TDG_XML_PGROUPS "property-groups"
+#define TDG_XML_PGROUP_NAME "property-group-name"
+#define TDG_XML_PRODUCT "product-id"
+#define TDG_XML_PROP_NAME TOPO_PROP_VAL_NAME
+#define TDG_XML_PROP_TYPE TOPO_PROP_VAL_TYPE
+#define TDG_XML_PROP_VALUE TOPO_PROP_VAL_VAL
+#define TDG_XML_PVALS "property-values"
+#define TDG_XML_OUTEDGES "outgoing-edges"
+#define TDG_XML_STRING "string"
+#define TDG_XML_STRING_ARR "string-array"
+#define TDG_XML_TOPO_DIGRAPH "topo-digraph"
+#define TDG_XML_TSTAMP "timestamp"
+#define TDG_XML_TYPE "type"
+#define TDG_XML_UINT8 "uint8"
+#define TDG_XML_UINT16 "uint16"
+#define TDG_XML_UINT32 "uint32"
+#define TDG_XML_UINT32_ARR "uint32-array"
+#define TDG_XML_UINT64 "uint64"
+#define TDG_XML_UINT64_ARR "uint64-array"
+#define TDG_XML_VALUE "value"
+#define TDG_XML_VERTEX "vertex"
+#define TDG_XML_VERTICES "vertices"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOPO_DIGRAPH_XML_H */
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
index c712cac908..13dfe11031 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
@@ -23,6 +23,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
#include <ctype.h>
#include <string.h>
@@ -31,6 +34,7 @@
#include <fm/fmd_fmri.h>
#include <sys/fm/protocol.h>
#include <topo_alloc.h>
+#include <topo_digraph.h>
#include <topo_error.h>
#include <topo_hc.h>
#include <topo_method.h>
@@ -132,20 +136,56 @@ int
topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
int *err)
{
- char *f, buf[PATH_MAX];
+ char *f, buf[PATH_MAX], *method = TOPO_METH_STR2NVL;
nvlist_t *out = NULL, *in = NULL;
tnode_t *rnode;
+ boolean_t is_path = B_FALSE;
- (void) strlcpy(buf, fmristr, sizeof (buf));
- if ((f = strchr(buf, ':')) == NULL)
- return (set_error(thp, ETOPO_FMRI_MALFORM, err,
- TOPO_METH_STR2NVL, in));
+ /*
+ * For path FMRI's the scheme is encoded in the authority portion of
+ * the FMRI - e.g.
+ *
+ * path://scheme=<scheme>/...
+ */
+ if (strncmp(fmristr, "path://", 7) == 0) {
+ char *scheme_start, *scheme_end;
- *f = '\0'; /* strip trailing FMRI path */
+ is_path = B_TRUE;
+ method = TOPO_METH_PATH_STR2NVL;
- if ((rnode = topo_hdl_root(thp, buf)) == NULL)
+ if ((scheme_start = strchr(fmristr, '=')) == NULL) {
+ return (set_error(thp, ETOPO_FMRI_MALFORM, err,
+ TOPO_METH_STR2NVL, in));
+ }
+ scheme_start++;
+ if ((scheme_end = strchr(scheme_start, '/')) == NULL) {
+ return (set_error(thp, ETOPO_FMRI_MALFORM, err,
+ TOPO_METH_STR2NVL, in));
+ }
+ (void) strlcpy(buf, scheme_start,
+ (scheme_end - scheme_start) + 1);
+ } else {
+ (void) strlcpy(buf, fmristr, sizeof (buf));
+
+ if ((f = strchr(buf, ':')) == NULL)
+ return (set_error(thp, ETOPO_FMRI_MALFORM, err,
+ TOPO_METH_STR2NVL, in));
+
+ *f = '\0'; /* strip trailing FMRI path */
+ }
+
+ if (is_path) {
+ topo_digraph_t *tdg;
+
+ if ((tdg = topo_digraph_get(thp, buf)) == NULL) {
+ return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
+ TOPO_METH_STR2NVL, in));
+ }
+ rnode = tdg->tdg_rootnode;
+ } else if ((rnode = topo_hdl_root(thp, buf)) == NULL) {
return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
TOPO_METH_STR2NVL, in));
+ }
if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
@@ -155,8 +195,8 @@ topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
in));
- if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
- TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
+ if (topo_method_invoke(rnode, method, TOPO_METH_STR2NVL_VERSION, in,
+ &out, err) != 0)
return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
nvlist_free(in);
@@ -711,7 +751,7 @@ topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
TOPO_METH_FMRI, NULL));
if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
- nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
+ nvlist_add_uint64(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
return (set_nverror(thp, ETOPO_FMRI_NVL, err,
TOPO_METH_FMRI, ins));
}
@@ -787,17 +827,17 @@ topo_fmri_next_auth(const char *auth)
* List of authority information we care about. Note that we explicitly ignore
* things that are properties of the chassis and not the resource itself:
*
- * FM_FMRI_AUTH_PRODUCT_SN "product-sn"
- * FM_FMRI_AUTH_PRODUCT "product-id"
- * FM_FMRI_AUTH_DOMAIN "domain-id"
- * FM_FMRI_AUTH_SERVER "server-id"
- * FM_FMRI_AUTH_HOST "host-id"
+ * FM_FMRI_AUTH_PRODUCT_SN "product-sn"
+ * FM_FMRI_AUTH_PRODUCT "product-id"
+ * FM_FMRI_AUTH_DOMAIN "domain-id"
+ * FM_FMRI_AUTH_SERVER "server-id"
+ * FM_FMRI_AUTH_HOST "host-id"
*
* We also ignore the "revision" authority member, as that typically indicates
* the firmware revision and is not a static property of the FRU. This leaves
* the following interesting members:
*
- * FM_FMRI_AUTH_CHASSIS "chassis-id"
+ * FM_FMRI_AUTH_CHASSIS "chassis-id"
* FM_FMRI_HC_SERIAL_ID "serial"
* FM_FMRI_HC_PART "part"
*/
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_method.h b/usr/src/lib/fm/topo/libtopo/common/topo_method.h
index c0ced1c188..1226e5e34a 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_method.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_method.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _TOPO_METHOD_H
#define _TOPO_METHOD_H
@@ -77,6 +77,9 @@ extern int topo_prop_method_version_register(tnode_t *, const char *,
#define TOPO_METH_PROP_SET "topo_prop_set"
#define TOPO_METH_FACILITY "topo_facility"
#define TOPO_METH_OCCUPIED "topo_occupied"
+#define TOPO_METH_PATH_STR2NVL "topo_path_str2nvl"
+#define TOPO_METH_PATH_NVL2STR "topo_path_nvl2str"
+
#define TOPO_METH_FMRI_VERSION 0
#define TOPO_METH_FRU_COMPUTE_VERSION 0
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
index 8f4a68c428..772c51bb0a 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
@@ -23,7 +23,7 @@
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _TOPO_MOD_H
@@ -296,6 +296,7 @@ typedef enum topo_mod_errno {
EMOD_NONCANON, /* non-canonical component name requested */
EMOD_MOD_NOENT, /* module lookup failed */
EMOD_UKNOWN_ENUM, /* unknown enumeration error */
+ EMOD_DIGRAPH_MAXSZ, /* max digraph size exceeded */
EMOD_END /* end of mod errno list (to ease auto-merge) */
} topo_mod_errno_t;
@@ -308,6 +309,19 @@ extern int topo_mod_file_search(topo_mod_t *, const char *file, int oflags);
extern topo_method_f topo_mod_hc_occupied;
+/*
+ * Directed Graph topology interfaces
+ */
+extern topo_digraph_t *topo_digraph_new(topo_hdl_t *, topo_mod_t *,
+ const char *);
+extern void topo_digraph_destroy(topo_digraph_t *);
+
+extern topo_vertex_t *topo_vertex_new(topo_mod_t *, const char *,
+ topo_instance_t);
+extern void topo_vertex_destroy(topo_mod_t *mod, topo_vertex_t *vtx);
+
+extern int topo_edge_new(topo_mod_t *, topo_vertex_t *, topo_vertex_t *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.map b/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
index b3652fcf49..040ba6a329 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.map
@@ -1,6 +1,6 @@
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
# CDDL HEADER START
#
@@ -25,6 +25,9 @@
$mapfile_version 2
SYMBOL_SCOPE {
+ topo_digraph_destroy { TYPE = FUNCTION; FLAGS = extern };
+ topo_digraph_new { TYPE = FUNCTION; FLAGS = extern };
+ topo_edge_new { TYPE = FUNCTION; FLAGS = extern };
topo_node_range_create { TYPE = FUNCTION; FLAGS = extern };
topo_node_range_destroy { TYPE = FUNCTION; FLAGS = extern };
topo_node_bind { TYPE = FUNCTION; FLAGS = extern };
@@ -101,4 +104,7 @@ SYMBOL_SCOPE {
topo_prop_inherit { TYPE = FUNCTION; FLAGS = extern };
topo_pgroup_create { TYPE = FUNCTION; FLAGS = extern };
topo_pgroup_hcset { TYPE = FUNCTION; FLAGS = extern };
+
+ topo_vertex_destroy { TYPE = FUNCTION; FLAGS = extern };
+ topo_vertex_new { TYPE = FUNCTION; FLAGS = extern };
};
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_node.c b/usr/src/lib/fm/topo/libtopo/common/topo_node.c
index 9a44e9e666..1d3460135d 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_node.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_node.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2019, Joyent, Inc. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
*/
/*
@@ -196,6 +196,14 @@ topo_node_destroy(tnode_t *node)
}
/*
+ * Nodes in a directed graph structure have no children, so the node
+ * name is still intact. We must free it now.
+ */
+ if (node->tn_vtx != NULL) {
+ topo_mod_strfree(mod, node->tn_name);
+ }
+
+ /*
* Destroy all property data structures, free the node and release
* the module that created it
*/
@@ -257,6 +265,12 @@ topo_node_parent(tnode_t *node)
return (node->tn_parent);
}
+topo_vertex_t *
+topo_node_vertex(tnode_t *node)
+{
+ return (node->tn_vtx);
+}
+
int
topo_node_flags(tnode_t *node)
{
@@ -315,7 +329,7 @@ topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
EMOD_NODE_DUP));
}
- if (min < 0 || max < min)
+ if (max < min)
return (node_create_seterror(mod, pnode, NULL,
EMOD_NODE_RANGE));
@@ -766,9 +780,7 @@ topo_node_unbind(tnode_t *node)
topo_node_unlock(node);
topo_dprintf(node->tn_hdl, TOPO_DBG_MODSVC,
- "node unbound %s=%d/%s=%d refs = %d\n",
- topo_node_name(node->tn_parent),
- topo_node_instance(node->tn_parent), node->tn_name,
+ "node unbound %s=%d refs = %d\n", node->tn_name,
node->tn_instance, node->tn_refs);
topo_node_rele(node);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
index fe2efb60f6..8557bcb331 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
/*
@@ -219,6 +219,7 @@ void
topo_close(topo_hdl_t *thp)
{
ttree_t *tp;
+ topo_digraph_t *tdg;
topo_hdl_lock(thp);
if (thp->th_platform != NULL)
@@ -252,6 +253,14 @@ topo_close(topo_hdl_t *thp)
}
/*
+ * Clean-up digraphs
+ */
+ while ((tdg = topo_list_next(&thp->th_digraphs)) != NULL) {
+ topo_list_delete(&thp->th_digraphs, tdg);
+ topo_digraph_destroy(tdg);
+ }
+
+ /*
* Unload all plugins
*/
topo_modhash_unload_all(thp);
@@ -426,6 +435,7 @@ topo_snap_destroy(topo_hdl_t *thp)
{
int i;
ttree_t *tp;
+ topo_digraph_t *tdg;
topo_walk_t *twp;
tnode_t *root;
topo_nodehash_t *nhp;
@@ -465,6 +475,29 @@ topo_snap_destroy(topo_hdl_t *thp)
}
+ for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL;
+ tdg = topo_list_next(tdg)) {
+
+ topo_vertex_t *vtx;
+
+ if (tdg->tdg_nvertices == 0)
+ continue;
+ /*
+ * We maintain an adjacency list in the topo_digraph_t
+ * structure, so we can just walk the list to destroy all the
+ * vertices.
+ */
+ mod = tdg->tdg_mod;
+ vtx = topo_list_next(&tdg->tdg_vertices);
+ while (vtx != NULL) {
+ topo_vertex_t *tmp = vtx;
+
+ vtx = topo_list_next(vtx);
+ topo_vertex_destroy(mod, tmp);
+ }
+ tdg->tdg_nvertices = 0;
+ }
+
/*
* Clean-up our cached devinfo and prom tree handles.
*/
@@ -477,7 +510,6 @@ topo_snap_destroy(topo_hdl_t *thp)
thp->th_pi = DI_PROM_HANDLE_NIL;
}
-
if (thp->th_uuid != NULL) {
topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE);
thp->th_uuid = NULL;
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
index a7afb3297a..bc59921f6c 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
@@ -23,7 +23,7 @@
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#include <alloca.h>
@@ -37,6 +37,7 @@
#include <sys/utsname.h>
#include <topo_error.h>
+#include <topo_digraph.h>
#include <topo_subr.h>
void
@@ -220,12 +221,18 @@ tnode_t *
topo_hdl_root(topo_hdl_t *thp, const char *scheme)
{
ttree_t *tp;
+ topo_digraph_t *tdg;
for (tp = topo_list_next(&thp->th_trees); tp != NULL;
tp = topo_list_next(tp)) {
if (strcmp(scheme, tp->tt_scheme) == 0)
return (tp->tt_root);
}
+ for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL;
+ tdg = topo_list_next(tdg)) {
+ if (strcmp(scheme, tdg->tdg_scheme) == 0)
+ return (tdg->tdg_rootnode);
+ }
return (NULL);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_tree.h b/usr/src/lib/fm/topo/libtopo/common/topo_tree.h
index 4c9c48cb04..c8e8d1d30d 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_tree.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_tree.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _TOPO_TREE_H
@@ -73,6 +73,7 @@ struct topo_node {
topo_list_t tn_methods; /* Registered method list */
void *tn_priv; /* Private enumerator data */
int tn_refs; /* node reference count */
+ topo_vertex_t *tn_vtx; /* NULL for tree topologies */
};
#define TOPO_NODE_INIT 0x0001
@@ -118,6 +119,7 @@ struct topo_hdl {
di_prom_handle_t th_pi; /* handle to root of prom tree */
topo_modhash_t *th_modhash; /* Module hash */
topo_list_t th_trees; /* Scheme-specific topo tree list */
+ topo_list_t th_digraphs; /* Scheme-specific topo digraph list */
topo_alloc_t *th_alloc; /* allocators */
int th_errno; /* errno */
int th_debug; /* Debug mask */
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_xml.c b/usr/src/lib/fm/topo/libtopo/common/topo_xml.c
index 648d5f3467..289d356171 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_xml.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_xml.c
@@ -104,16 +104,18 @@ xmlattr_to_int(topo_mod_t *mp,
xmlChar *str;
xmlChar *estr;
- topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_int(propname=%s)\n",
- propname);
- if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
+ if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) {
+ topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
+ "%s: failed to lookup %s attribute", __func__, propname);
return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
-
+ }
errno = 0;
*value = strtoull((char *)str, (char **)&estr, 0);
if (errno != 0 || *estr != '\0') {
/* no conversion was done */
xmlFree(str);
+ topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
+ "%s: failed to convert %s attribute", __func__, propname);
return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
}
xmlFree(str);
diff --git a/usr/src/lib/fm/topo/maps/Makefile.map b/usr/src/lib/fm/topo/maps/Makefile.map
index 8c1c33cca2..0ed01490ec 100644
--- a/usr/src/lib/fm/topo/maps/Makefile.map
+++ b/usr/src/lib/fm/topo/maps/Makefile.map
@@ -22,7 +22,8 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+#
+# Copyright 2020 Joyent, Inc.
#
.KEEP_STATE:
@@ -38,6 +39,11 @@ DTDSRC = $(DTDFILE:%=../common/%)
DTDTARG = $(DTDFILE:%=%)
ROOTDTDTARG = $(DTDTARG:%=$(ROOT)/usr/share/lib/xml/dtd/%)
+DIGRAPH_DTD = digraph-topology.dtd.1
+DIGRAPH_DTD_SRC = ../common/$(DIGRAPH_DTD)
+ROOT_DTD_DIR=$(ROOT)/usr/share/lib/xml/dtd
+ROOT_DIGRAPH_DTD=$(ROOT_DTD_DIR)/$(DIGRAPH_DTD)
+
common_ROOTTOPOROOT = $(ROOT)/usr/lib/fm/topo/$(MODCLASS)
arch_ROOTTOPOROOT = $(ROOT)/usr/platform/$(ARCH)/lib/fm/topo/$(MODCLASS)
platform_ROOTTOPOROOT = \
@@ -51,7 +57,7 @@ install:= FILEMODE = 0444
# to avoid having to deal with things like 48 platform specific internal
# storage bays by hand.
.xmlgen.xml:
- $(RM) $@
+ $(RM) $@
$(CAT) ../common/xmlgen-header.xml > $@
$(PERL) $< >> $@
@@ -59,7 +65,7 @@ install:= FILEMODE = 0444
$(RM) $@
$(CAT) ../common/xmlgen-header-new.xml > $@
$(KSH93) $< >> $@
-
+
%.xml: ../common/%.xml
$(RM) $@
$(CAT) $< > $@
@@ -92,4 +98,7 @@ $($(CLASS)_ROOTTOPOROOT)/%: %
$(ROOTDTDTARG): $$(@D)
$(RM) $@; $(INS) -s -m 0444 -f $(@D) $(DTDSRC)
-install: all $(ROOTDTDTARG) $(ROOTTOPOROOT) $(ROOTTOPOMAPS)
+$(ROOT_DIGRAPH_DTD): $(DIGRAPH_DTD_SRC)
+ $(RM) $@; $(INS) -s -m 0444 -f $(ROOT_DTD_DIR) $(DIGRAPH_DTD_SRC)
+
+install: all $(ROOTDTDTARG) $(ROOT_DIGRAPH_DTD) $(ROOTTOPOROOT) $(ROOTTOPOMAPS)
diff --git a/usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1 b/usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
new file mode 100644
index 0000000000..453a82a5e1
--- /dev/null
+++ b/usr/src/lib/fm/topo/maps/common/digraph-topology.dtd.1
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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 2020 Joyent, Inc.
+-->
+
+<!--
+ DTD for Directed Graph based topologies
+-->
+
+<!ELEMENT topo-digraph (vertices) >
+
+<!ATTLIST topo-digraph
+ scheme CDATA #REQUIRED
+>
+
+<!ELEMENT vertices>
+
+<!ATTLIST vertices
+ nelem CDATA #REQUIRED
+>
+
+<!ELEMENT vertex (nvpair*, outgoing-edges?) >
+
+<!ELEMENT nvlist (nvpair*) >
+
+<!ELEMENT nvpair (nvlist?) >
+
+<!ATTLIST nvpair
+ name CDATA #REQUIRED
+ type ( int8 | uint8 | int16 | uint16 | int32 | uint32 |
+ int64 | uint64 | string | nvlist | int32-array |
+ uint32-array | int64-array | uint64-array |
+ string-array | nvlist-array | "")
+ value CDATA ""
+>
+
+<!ELEMENT outgoing-edges (edge*) >
+
+<!ELEMENT edge (edge*) >
+
+<!ATTLIST edge
+ fmri CDATA #REQUIRED
+>
diff --git a/usr/src/lib/fm/topo/modules/common/ses/ses.c b/usr/src/lib/fm/topo/modules/common/ses/ses.c
index 5fa0905d7b..6b9be28bd5 100644
--- a/usr/src/lib/fm/topo/modules/common/ses/ses.c
+++ b/usr/src/lib/fm/topo/modules/common/ses/ses.c
@@ -66,6 +66,8 @@ static int ses_snap_freq = 250; /* in milliseconds */
#define HR_SECOND 1000000000
+#define SES_INST_NOTSET UINT64_MAX
+
/*
* Because multiple SES targets can be part of a single chassis, we construct
* our own hierarchy that takes this into account. These SES targets may refer
@@ -3166,7 +3168,7 @@ ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
goto error;
}
- if (cp->sec_maxinstance >= 0 &&
+ if (cp->sec_maxinstance != SES_INST_NOTSET &&
(topo_node_range_create(mod, tn, SUBCHASSIS, 0,
cp->sec_maxinstance) != 0)) {
topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
@@ -3405,7 +3407,7 @@ ses_enum_gather(ses_node_t *np, void *data)
goto error;
cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
- cp->sec_maxinstance = -1;
+ cp->sec_maxinstance = SES_INST_NOTSET;
cp->sec_csn = csn;
if (subchassis == NO_SUBCHASSIS) {
diff --git a/usr/src/lib/libpcsc/sparc/Makefile b/usr/src/lib/libpcsc/sparc/Makefile
new file mode 100644
index 0000000000..e4bd71f624
--- /dev/null
+++ b/usr/src/lib/libpcsc/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 2019 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libpcsc/sparcv9/Makefile b/usr/src/lib/libpcsc/sparcv9/Makefile
new file mode 100644
index 0000000000..a32567f965
--- /dev/null
+++ b/usr/src/lib/libpcsc/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 2019 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/man/man3kstat/kstat_chain_update.3kstat b/usr/src/man/man3kstat/kstat_chain_update.3kstat
index 5a4a0656d3..012a4f3b08 100644
--- a/usr/src/man/man3kstat/kstat_chain_update.3kstat
+++ b/usr/src/man/man3kstat/kstat_chain_update.3kstat
@@ -7,7 +7,6 @@
.SH NAME
kstat_chain_update \- update the kstat header chain
.SH SYNOPSIS
-.LP
.nf
\fBcc\fR [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-lkstat\fR [ \fIlibrary\fR\&.\|.\|.]
#include <kstat.h>
@@ -18,8 +17,6 @@ kstat_chain_update \- update the kstat header chain
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBkstat_chain_update()\fR function brings the user's kstat header chain in
sync with that of the kernel. The kstat chain is a linked list of kstat headers
(\fBkstat_t\fR's) pointed to by \fIkc\fR->\fBkc_chain\fR, which is initialized
@@ -27,7 +24,7 @@ by \fBkstat_open\fR(3KSTAT). This chain constitutes a list of all kstats
currently in the system.
.sp
.LP
-During normal operation, the kernel creates new kstats and delete old ones as
+During normal operation, the kernel creates new kstats and deletes old ones as
various device instances are added and removed, thereby causing the user's copy
of the kstat chain to become out of date. The \fBkstat_chain_update()\fR
function detects this condition by comparing the kernel's current kstat chain
@@ -37,15 +34,18 @@ user's KCID, \fIkc\fR->\fBkc_chain_id\fR. If the KCIDs match,
kstat headers from the user's kstat chain, adds any new ones, and sets
\fIkc\fR->\fBkc_chain_id\fR to the new KCID. All other kstat headers in the
user's kstat chain are unmodified.
-.SH RETURN VALUES
.sp
.LP
+Because of the deletion process, any \fBkstat_t\fR structures retrieved by the
+\fBkstat_lookup\fR(3KSTAT) function or data pointers obtained through the
+\fBkstat_data_lookup\fR(3KSTAT) function are invalidated by a call to
+\fRkstat_chain_update\fB(). New lookup calls must be made to obtain fresh
+structures after each chain update.
+.SH RETURN VALUES
Upon successful completion, \fBkstat_chain_update()\fR returns the new KCID if
the kstat chain has changed and 0 if it has not changed. Otherwise, it returns
\(mi1 and sets \fBerrno\fR to indicate the error.
.SH ERRORS
-.sp
-.LP
The \fBkstat_chain_update()\fR function will fail if:
.sp
.ne 2
@@ -84,7 +84,6 @@ The data for the given kstat was too large to be stored in the structure.
.RE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/dev/kstat\fR\fR
@@ -94,8 +93,6 @@ kernel statistics driver
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -112,7 +109,5 @@ MT-Level Unsafe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBkstat\fR(3KSTAT), \fBkstat_lookup\fR(3KSTAT), \fBkstat_open\fR(3KSTAT),
\fBkstat_read\fR(3KSTAT), \fBattributes\fR(5)
diff --git a/usr/src/man/man3kstat/kstat_lookup.3kstat b/usr/src/man/man3kstat/kstat_lookup.3kstat
index 3458f61f21..7a0f9880e7 100644
--- a/usr/src/man/man3kstat/kstat_lookup.3kstat
+++ b/usr/src/man/man3kstat/kstat_lookup.3kstat
@@ -3,11 +3,10 @@
.\" 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]
-.TH KSTAT_LOOKUP 3KSTAT "Aug 3, 2004"
+.TH KSTAT_LOOKUP 3KSTAT "March 24, 2020"
.SH NAME
kstat_lookup, kstat_data_lookup \- find a kstat by name
.SH SYNOPSIS
-.LP
.nf
\fBcc\fR [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-lkstat\fR [ \fIlibrary\fR\&.\|.\|.]
#include <kstat.h>
@@ -22,8 +21,6 @@ kstat_lookup, kstat_data_lookup \- find a kstat by name
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBkstat_lookup()\fR function traverses the kstat chain,
\fIkc\fR->\fBkc_chain\fR, searching for a kstat with the same \fIks_module\fR,
\fIks_instance\fR, and \fIks_name\fR fields; this triplet uniquely identifies a
@@ -37,9 +34,15 @@ The \fBkstat_data_lookup()\fR function searches the kstat's data section for
the record with the specified \fIname\fR. This operation is valid only for
those kstat types that have named data records: \fBKSTAT_TYPE_NAMED\fR and
\fBKSTAT_TYPE_TIMER\fR.
+.SS Lifetime
+The \fBkstat_t\fR structures and any associated data are owned by the library
+and the corresponding handle, \fIkc\fR. That is, two callers with same library
+handle will generally have the same memory returned to them, though this is not
+a guaranteed part of the interface. Callers should not modify or attempt to free
+the data associated with either. Calling the \fBkstat_chain_update\fR(3KSTAT) or
+\fBkstat_close\fR(3KSTAT) functions on the handle \fIkc\fR will cause the
+pointers returned from these functions with the same handle to be invalid.
.SH RETURN VALUES
-.sp
-.LP
The \fBkstat_lookup()\fR function returns a pointer to the requested kstat if
it is found. Otherwise it returns \fINULL\fR and sets \fBerrno\fR to indicate
the error.
@@ -49,8 +52,6 @@ The \fBkstat_data_lookup()\fR function returns a pointer to the requested data
record if it is found. Otherwise it returns \fINULL\fR and sets \fBerrno\fR to
indicate the error .
.SH ERRORS
-.sp
-.LP
The \fBkstat_lookup()\fR and \fBkstat_data_lookup()\fR functions will fail if:
.sp
.ne 2
@@ -72,7 +73,6 @@ The requested kstat could not be found.
.RE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/dev/kstat\fR\fR
@@ -82,8 +82,6 @@ kernel statistics driver
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -100,7 +98,5 @@ MT-Level Unsafe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBkstat\fR(3KSTAT), \fBkstat_chain_update\fR(3KSTAT),
\fBkstat_open\fR(3KSTAT), \fBkstat_read\fR(3KSTAT), \fBattributes\fR(5)
diff --git a/usr/src/man/man7d/amdf17nbdf.7d b/usr/src/man/man7d/amdf17nbdf.7d
index 739eab6c82..e6e6ce863d 100644
--- a/usr/src/man/man7d/amdf17nbdf.7d
+++ b/usr/src/man/man7d/amdf17nbdf.7d
@@ -11,7 +11,7 @@
.\"
.\" Copyright 2019, Joyent, Inc.
.\"
-.Dd March 20, 2019
+.Dd November 16, 2019
.Dt AMDF17NBDF 7D
.Os
.Sh NAME
@@ -24,7 +24,7 @@ The
.Nm
driver provides the system access to the Northbridge and Data Fabric
devices on AMD Family 17h
-.Pq Zen
+.Pq Zen, Zen+, and Zen 2
processors allowing the operating system to communicate with the system
management unit
.Pq SMU .
diff --git a/usr/src/pkg/manifests/driver-cpu-sensor.mf b/usr/src/pkg/manifests/driver-cpu-sensor.mf
index 5ce38d340f..4deea9570f 100644
--- a/usr/src/pkg/manifests/driver-cpu-sensor.mf
+++ b/usr/src/pkg/manifests/driver-cpu-sensor.mf
@@ -30,8 +30,13 @@ dir path=usr/lib/devfsadm/linkmod group=sys
dir path=usr/share/man
dir path=usr/share/man/man7d
driver name=amdf17nbdf \
+ alias=pci1022,1440,p \
alias=pci1022,1450,p \
- alias=pci1022,1460,p
+ alias=pci1022,1460,p \
+ alias=pci1022,1480,p \
+ alias=pci1022,1490,p \
+ alias=pci1022,15d0,p \
+ alias=pci1022,15e8,p
driver name=amdnbtemp \
alias=pci1022,1203,p \
alias=pci1022,1303,p \
diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf
index 275d8c5661..2651d7e519 100644
--- a/usr/src/pkg/manifests/service-fault-management.mf
+++ b/usr/src/pkg/manifests/service-fault-management.mf
@@ -911,8 +911,10 @@ file path=usr/sbin/fmadm mode=0555 variant.opensolaris.zone=__NODEFAULT
file path=usr/sbin/fmdump mode=0555 variant.opensolaris.zone=__NODEFAULT
file path=usr/sbin/fmstat mode=0555 variant.opensolaris.zone=__NODEFAULT
#
-# Topo DTD is also common
+# Topo DTDs are also common
#
+file path=usr/share/lib/xml/dtd/digraph-topology.dtd.1 \
+ variant.opensolaris.zone=__NODEFAULT
file path=usr/share/lib/xml/dtd/topology.dtd.1 \
variant.opensolaris.zone=__NODEFAULT
file path=usr/share/man/man1m/fmadm.1m
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index cb41255b7a..5b1da26c58 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -386,7 +386,9 @@ file path=usr/include/gssapi/gssapi_ext.h
file path=usr/include/hal/libhal-storage.h
file path=usr/include/hal/libhal.h
$(i386_ONLY)file path=usr/include/ia32/sys/asm_linkage.h
+$(i386_ONLY)file path=usr/include/ia32/sys/kdi_regs.h
$(i386_ONLY)file path=usr/include/ia32/sys/machtypes.h
+$(i386_ONLY)file path=usr/include/ia32/sys/privmregs.h
$(i386_ONLY)file path=usr/include/ia32/sys/privregs.h
$(i386_ONLY)file path=usr/include/ia32/sys/psw.h
$(i386_ONLY)file path=usr/include/ia32/sys/pte.h
diff --git a/usr/src/pkg/manifests/system-test-ostest.mf b/usr/src/pkg/manifests/system-test-ostest.mf
index 6e20cc2b8e..6b23290d06 100644
--- a/usr/src/pkg/manifests/system-test-ostest.mf
+++ b/usr/src/pkg/manifests/system-test-ostest.mf
@@ -28,6 +28,7 @@ dir path=opt/os-tests/tests
dir path=opt/os-tests/tests/ddi_ufm
dir path=opt/os-tests/tests/file-locking
$(i386_ONLY)dir path=opt/os-tests/tests/i386
+dir path=opt/os-tests/tests/libtopo
dir path=opt/os-tests/tests/pf_key
dir path=opt/os-tests/tests/poll
dir path=opt/os-tests/tests/sdevfs
@@ -53,6 +54,12 @@ $(i386_ONLY)file path=opt/os-tests/tests/i386/badseg mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/i386/badseg_exec mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/i386/ldt mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/imc_test mode=0555
+file path=opt/os-tests/tests/libtopo/digraph-test mode=0555
+file path=opt/os-tests/tests/libtopo/digraph-test-in-badedge.xml mode=0444
+file path=opt/os-tests/tests/libtopo/digraph-test-in-badelement.xml mode=0444
+file path=opt/os-tests/tests/libtopo/digraph-test-in-badnum.xml mode=0444
+file path=opt/os-tests/tests/libtopo/digraph-test-in-badscheme.xml mode=0444
+file path=opt/os-tests/tests/libtopo/digraph-test-in.xml mode=0444
file path=opt/os-tests/tests/odirectory.32 mode=0555
file path=opt/os-tests/tests/odirectory.64 mode=0555
file path=opt/os-tests/tests/pf_key/acquire-compare mode=0555
diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run
index 2313f65f7b..7e1dffde90 100644
--- a/usr/src/test/os-tests/runfiles/default.run
+++ b/usr/src/test/os-tests/runfiles/default.run
@@ -112,3 +112,7 @@ arch = i86pc
[/opt/os-tests/tests/uccid]
arch = i86pc
tests = ['atrparse']
+
+[/opt/os-tests/tests/libtopo]
+user = root
+tests = ['digraph-test']
diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile
index e42bdea1fa..316ae78b2e 100644
--- a/usr/src/test/os-tests/tests/Makefile
+++ b/usr/src/test/os-tests/tests/Makefile
@@ -19,6 +19,7 @@ SUBDIRS_i386 = i386 imc
SUBDIRS = \
ddi_ufm \
file-locking \
+ libtopo \
pf_key \
poll \
sdevfs \
diff --git a/usr/src/test/os-tests/tests/libtopo/Makefile b/usr/src/test/os-tests/tests/libtopo/Makefile
new file mode 100644
index 0000000000..8b7fe43bb7
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/Makefile
@@ -0,0 +1,58 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/libtopo
+
+PROGS = digraph-test
+
+XML = digraph-test-in.xml \
+ digraph-test-in-badscheme.xml \
+ digraph-test-in-badnum.xml \
+ digraph-test-in-badedge.xml \
+ digraph-test-in-badelement.xml
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+LDLIBS += -L$(ROOT)/usr/lib/fm -ltopo -R/usr/lib/fm
+CFLAGS += -I$(SRC)/lib/fm/topo/libtopo/
+CSTD= $(CSTD_GNU99)
+
+CMDS = $(PROGS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+FILES = $(XML:%=$(TESTDIR)/%)
+$(FILES) := FILEMODE = 0444
+
+all: $(PROGS)
+
+install: all $(CMDS) $(FILES)
+
+clobber: clean
+ -$(RM) $(PROGS) $(FILES)
+
+clean:
+ -$(RM) *.o
+
+$(CMDS): $(TESTDIR) $(PROGS)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badedge.xml b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badedge.xml
new file mode 100644
index 0000000000..db86874817
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badedge.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/digraph-topology.dtd.1">
+
+<!--
+ Copyright 2020 Joyent, Inc.
+
+-->
+
+<topo-digraph fmri-scheme='hc' nodename='test-nodename' os-version='test-os-version' product-id='test-product-id' timestamp='2019-12-20T01:51:26Z'>
+<vertices>
+<vertex name='node' instance='0x0' fmri='hc:///node=0'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='0' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+
+ <outgoing-edges>
+ <!-- non-existent edge -->
+ <edge fmri='hc:///node=1' />
+ </outgoing-edges>
+
+</vertex>
+
+</vertices>
+</topo-digraph>
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badelement.xml b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badelement.xml
new file mode 100644
index 0000000000..374bcc85cc
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badelement.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/digraph-topology.dtd.1">
+
+<!--
+ Copyright 2020 Joyent, Inc.
+
+-->
+
+<topo-digraph fmri-scheme='hc' nodename='test-nodename' os-version='test-os-version' product-id='test-product-id' timestamp='2019-12-20T01:51:26Z'>
+<vertices>
+<vertex name='node' instance='0x0' fmri='hc:///node=0'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <!-- DTD violation: bad element -->
+ <badelement>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='0' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+
+</vertex>
+
+</vertices>
+</topo-digraph>
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badnum.xml b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badnum.xml
new file mode 100644
index 0000000000..28f737dfb6
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badnum.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/digraph-topology.dtd.1">
+
+<!--
+ Copyright 2020 Joyent, Inc.
+
+-->
+
+<topo-digraph fmri-scheme='hc' nodename='test-nodename' os-version='test-os-version' product-id='test-product-id' timestamp='2019-12-20T01:51:26Z'>
+<vertices>
+<vertex name='node' instance='0x0' fmri='hc:///node=0'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <!-- bad numeric value -->
+ <nvpair name='property-type' type='uint32' value='gdfgdfgdffg' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='0' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+
+</vertex>
+
+</vertices>
+</topo-digraph>
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badscheme.xml b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badscheme.xml
new file mode 100644
index 0000000000..254519692d
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test-in-badscheme.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/digraph-topology.dtd.1">
+
+<!--
+ Copyright 2020 Joyent, Inc.
+
+-->
+
+<!-- invalid fmri-scheme value -->
+<topo-digraph fmri-scheme='badscheme' nodename='test-nodename' os-version='test-os-version' product-id='test-product-id' timestamp='2019-12-20T01:51:26Z'>
+<vertices>
+<vertex name='node' instance='0x0' fmri='hc:///node=0'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='0' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+
+</vertex>
+
+</vertices>
+</topo-digraph>
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test-in.xml b/usr/src/test/os-tests/tests/libtopo/digraph-test-in.xml
new file mode 100644
index 0000000000..679e1f834b
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test-in.xml
@@ -0,0 +1,308 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/digraph-topology.dtd.1">
+
+<!--
+ Copyright 2020 Joyent, Inc.
+
+ This XML represents a directed graph that looks like the following:
+
+ |===> node=2 ===
+ | |
+ node=0 ====> node=1 === |===> node=4 ==
+ | | |
+ |===> node=3 === |
+ ^ ^ |
+ node=5 | | |
+ | | |
+ node=6 ======================= ======================
+
+-->
+
+<topo-digraph fmri-scheme='hc' nodename='test-nodename' os-version='test-os-version' product-id='test-product-id' timestamp='2019-12-20T01:51:26Z'>
+<vertices>
+<vertex name='node' instance='0x0' fmri='hc:///node=0'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='test-pg-1' />
+ <nvpair name='property-values' type='nvlist-array'>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='string-prop' />
+ <nvpair name='property-type' type='uint32' value='6' />
+ <nvpair name='property-value' type='string' value='blahblahblah' />
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='uint64-array-prop' />
+ <nvpair name='property-type' type='uint32' value='13' />
+ <nvpair name='property-value' type='uint64-array'>
+ <nvpair value='0x1' />
+ <nvpair value='0x2' />
+ </nvpair>
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='int64-array-prop' />
+ <nvpair name='property-type' type='uint32' value='12' />
+ <nvpair name='property-value' type='int64-array'>
+ <nvpair value='1' />
+ <nvpair value='2' />
+ </nvpair>
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='uint32-array-prop' />
+ <nvpair name='property-type' type='uint32' value='11' />
+ <nvpair name='property-value' type='uint32-array'>
+ <nvpair value='1' />
+ <nvpair value='2' />
+ </nvpair>
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='int32-array-prop' />
+ <nvpair name='property-type' type='uint32' value='10' />
+ <nvpair name='property-value' type='int32-array'>
+ <nvpair value='1' />
+ <nvpair value='2' />
+ </nvpair>
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='uint64-prop' />
+ <nvpair name='property-type' type='uint32' value='5' />
+ <nvpair name='property-value' type='uint64' value='0x5003048023567a00' />
+ </nvlist>
+
+ <nvlist>
+ <nvpair name='property-name' type='string' value='uint32-prop' />
+ <nvpair name='property-type' type='uint32' value='3' />
+ <nvpair name='property-value' type='uint32' value='1' />
+ </nvlist>
+
+ </nvpair> <!-- property-values -->
+ </nvlist>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='0' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=1' />
+ </outgoing-edges>
+
+</vertex>
+
+<vertex name='node' instance='0x1' fmri='hc:///node=1'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='1' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=2' />
+ <edge fmri='hc:///node=3' />
+ </outgoing-edges>
+
+</vertex>
+
+<vertex name='node' instance='0x2' fmri='hc:///node=2'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='2' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=4' />
+ </outgoing-edges>
+
+</vertex>
+
+<vertex name='node' instance='0x3' fmri='hc:///node=3'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='3' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=4' />
+ </outgoing-edges>
+
+</vertex>
+
+<vertex name='node' instance='0x4' fmri='hc:///node=4'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='4' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=3' />
+ </outgoing-edges>
+
+</vertex>
+
+<vertex name='node' instance='0x5' fmri='hc:///node=5'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='5' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+
+</vertex>
+
+<vertex name='node' instance='0x6' fmri='hc:///node=6'>
+
+ <nvpair name='property-groups' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-group-name' type='string' value='protocol' />
+ <nvpair name='property-values' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='property-name' type='string' value='resource' />
+ <nvpair name='property-type' type='uint32' value='9' />
+ <nvpair name='property-value' type='nvlist'>
+ <nvlist>
+ <nvpair name='scheme' type='string' value='hc' />
+ <nvpair name='version' type='uint8' value='0' />
+ <nvpair name='hc-list' type='nvlist-array'>
+ <nvlist>
+ <nvpair name='hc-name' type='string' value='node' />
+ <nvpair name='hc-id' type='string' value='6' />
+ </nvlist>
+ </nvpair>
+ </nvlist>
+ </nvpair> <!-- property-value -->
+ </nvlist>
+ </nvpair> <!-- property-values -->
+ </nvlist>
+
+ </nvpair> <!-- property-groups -->
+ <outgoing-edges>
+ <edge fmri='hc:///node=3' />
+ </outgoing-edges>
+
+</vertex>
+
+</vertices>
+</topo-digraph>
diff --git a/usr/src/test/os-tests/tests/libtopo/digraph-test.c b/usr/src/test/os-tests/tests/libtopo/digraph-test.c
new file mode 100644
index 0000000000..b829f0bb8f
--- /dev/null
+++ b/usr/src/test/os-tests/tests/libtopo/digraph-test.c
@@ -0,0 +1,380 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <fm/libtopo.h>
+#include <sys/debug.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+
+
+#define TEST_HOME "/opt/os-tests/tests/libtopo/"
+#define TEST_XML_IN "digraph-test-in.xml"
+#define TEST_XML_IN_BADSCHEME "digraph-test-in-badscheme.xml"
+#define TEST_XML_IN_BADNUM "digraph-test-in-badnum.xml"
+#define TEST_XML_IN_BADEDGE "digraph-test-in-badedge.xml"
+#define TEST_XML_IN_BADELEMENT "digraph-test-in-badelement.xml"
+#define TEST_GRAPH_SZ 7
+#define TEST_XML_OUT_DIR "/var/tmp"
+#define TEST_XML_OUT_PREFIX "digraph-test-out"
+
+static const char *pname;
+
+extern int topo_hdl_errno(topo_hdl_t *);
+
+/*
+ * Generate an ISO 8601 timestamp
+ */
+static void
+get_timestamp(char *buf, size_t bufsize)
+{
+ time_t utc_time;
+ struct tm *p_tm;
+
+ (void) time(&utc_time);
+ p_tm = localtime(&utc_time);
+
+ (void) strftime(buf, bufsize, "%FT%TZ", p_tm);
+}
+
+/* PRINTFLIKE1 */
+static void
+logmsg(const char *format, ...)
+{
+ char timestamp[128];
+ va_list ap;
+
+ get_timestamp(timestamp, sizeof (timestamp));
+ (void) fprintf(stdout, "%s ", timestamp);
+ va_start(ap, format);
+ (void) vfprintf(stdout, format, ap);
+ va_end(ap);
+ (void) fprintf(stdout, "\n");
+ (void) fflush(stdout);
+}
+
+static topo_digraph_t *
+test_deserialize(topo_hdl_t *thp, const char *path)
+{
+ struct stat statbuf = { 0 };
+ char *buf = NULL;
+ int fd = -1;
+ topo_digraph_t *tdg = NULL;
+
+ logmsg("\tOpening test XML topology");
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ logmsg("\tfailed to open %s (%s)", path, strerror(errno));
+ goto out;
+ }
+ if (fstat(fd, &statbuf) != 0) {
+ logmsg("\tfailed to stat %s (%s)", path, strerror(errno));
+ goto out;
+ }
+ if ((buf = malloc(statbuf.st_size)) == NULL) {
+ logmsg("\tfailed to alloc read buffer: (%s)", strerror(errno));
+ goto out;
+ }
+ if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
+ logmsg("\tfailed to read file: (%s)", strerror(errno));
+ goto out;
+ }
+
+ logmsg("\tDeserializing XML topology");
+ tdg = topo_digraph_deserialize(thp, buf, statbuf.st_size);
+ if (tdg == NULL) {
+ logmsg("\ttopo_digraph_deserialize() failed!");
+ goto out;
+ }
+ logmsg("\ttopo_digraph_deserialize() succeeded");
+out:
+ free(buf);
+ if (fd > 0) {
+ (void) close(fd);
+ }
+ return (tdg);
+}
+
+struct cb_arg {
+ topo_vertex_t **vertices;
+};
+
+static int
+test_paths_cb(topo_hdl_t *thp, topo_vertex_t *vtx, boolean_t last_vtx,
+ void *arg)
+{
+ struct cb_arg *cbarg = arg;
+ uint_t idx = topo_node_instance(topo_vertex_node(vtx));
+
+ cbarg->vertices[idx] = vtx;
+
+ return (TOPO_WALK_NEXT);
+}
+
+static int
+test_paths(topo_hdl_t *thp, topo_digraph_t *tdg)
+{
+ topo_vertex_t *vertices[TEST_GRAPH_SZ];
+ struct cb_arg cbarg = { 0 };
+ int ret = -1;
+ topo_path_t **paths;
+ uint_t np;
+
+ cbarg.vertices = vertices;
+ if (topo_vertex_iter(thp, tdg, test_paths_cb, &cbarg) != 0) {
+ logmsg("\tfailed to iterate over graph vertices");
+ goto out;
+ }
+
+ logmsg("\tCalculating number of paths between node 0 and node 4");
+ if (topo_digraph_paths(thp, tdg, vertices[0], vertices[4], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 2) {
+ logmsg("\t%d paths found (expected 2)", np);
+ goto out;
+ }
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+
+ logmsg("\tCalculating number of paths between node 6 and node 4");
+ if (topo_digraph_paths(thp, tdg, vertices[6], vertices[4], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 1) {
+ logmsg("\t%d paths found (expected 1)", np);
+ goto out;
+ }
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+
+ logmsg("\tCalculating number of paths between node 5 and node 1");
+ if (topo_digraph_paths(thp, tdg, vertices[5], vertices[1], &paths,
+ &np) < 0) {
+ logmsg("\ttopo_digraph_paths() failed");
+ goto out;
+ }
+ if (np != 0) {
+ logmsg("\t%d paths found (expected 0)", np);
+ goto out;
+ }
+ ret = 0;
+
+out:
+ if (np > 0) {
+ for (uint_t i = 0; i < np; i++) {
+ topo_path_destroy(thp, paths[i]);
+ }
+ topo_hdl_free(thp, paths, np * sizeof (topo_path_t *));
+ }
+ return (ret);
+}
+
+static int
+test_serialize(topo_hdl_t *thp, topo_digraph_t *tdg, const char *path)
+{
+ FILE *xml_out;
+
+ if ((xml_out = fopen(path, "w")) == NULL) {
+ logmsg("\tfailed to open %s for writing (%s)",
+ strerror(errno));
+ return (-1);
+ }
+ logmsg("\tSerializing topology to XML (%s)", path);
+ if (topo_digraph_serialize(thp, tdg, xml_out) != 0) {
+ logmsg("\ttopo_digraph_serialize() failed!");
+ (void) fclose(xml_out);
+ return (-1);
+ }
+ (void) fclose(xml_out);
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ topo_hdl_t *thp = NULL;
+ topo_digraph_t *tdg;
+ char *root = "/", *out_path = NULL;
+ boolean_t abort_on_exit = B_FALSE;
+ int err, status = EXIT_FAILURE;
+
+ pname = argv[0];
+
+ /*
+ * Setting DIGRAPH_TEST_CORE causes us to abort and dump core before
+ * exiting. This is useful for examining for memory leaks.
+ */
+ if (getenv("DIGRAPH_TEST_CORE") != NULL) {
+ abort_on_exit = B_TRUE;
+ }
+
+ logmsg("Opening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ logmsg("TEST: Deserialize directed graph topology");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN)) == NULL) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("TEST: Serialize directed graph topology");
+ if ((out_path = tempnam(TEST_XML_OUT_DIR, TEST_XML_OUT_PREFIX)) ==
+ NULL) {
+ logmsg("\tFailed to create temporary file name under %s (%s)",
+ TEST_XML_OUT_DIR, strerror(errno));
+ logmsg("FAIL");
+ goto out;
+ }
+ if (test_serialize(thp, tdg, out_path) != 0) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("Closing libtopo");
+ topo_close(thp);
+
+ logmsg("Reopening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (pass 2)");
+ if ((tdg = test_deserialize(thp, out_path)) == NULL) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("TEST: Calculating paths between vertices");
+ if (test_paths(thp, tdg) != 0) {
+ logmsg("FAIL");
+ goto out;
+ }
+ logmsg("PASS");
+
+ logmsg("Closing libtopo");
+ topo_close(thp);
+
+ logmsg("Reopening libtopo");
+ if ((thp = topo_open(TOPO_VERSION, root, &err)) == NULL) {
+ logmsg("failed to get topo handle: %s", topo_strerror(err));
+ goto out;
+ }
+
+ /*
+ * The following tests attempt to deserialize XML files that either
+ * violate the DTD or contain invalid attribute values.
+ *
+ * The expection is that topo_digraph_deserialize() should fail
+ * gracefully (i.e. not segfault) and topo_errno should be set.
+ */
+ logmsg("TEST: Deserialize directed graph topology (bad scheme)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADSCHEME)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad number)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADNUM)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad edge)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADEDGE)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ logmsg("TEST: Deserialize directed graph topology (bad element)");
+ if ((tdg = test_deserialize(thp, TEST_HOME TEST_XML_IN_BADELEMENT)) !=
+ NULL) {
+ logmsg("FAIL");
+ goto out;
+ } else if (topo_hdl_errno(thp) == 0) {
+ logmsg("\texpected topo_errno to be non-zero");
+ logmsg("FAIL");
+ goto out;
+ } else {
+ logmsg("PASS");
+ }
+
+ /*
+ * If any tests failed, we don't unlink the temp file, as its contents
+ * may be useful for root-causing the test failure.
+ */
+ if (unlink(out_path) != 0) {
+ logmsg("Failed to unlink temp file: %s (%s)", out_path,
+ strerror(errno));
+ }
+ status = EXIT_SUCCESS;
+out:
+ if (thp != NULL) {
+ topo_close(thp);
+ }
+ if (out_path != NULL) {
+ free(out_path);
+ }
+ logmsg("digraph tests %s",
+ status == EXIT_SUCCESS ? "passed" : "failed");
+
+ if (abort_on_exit) {
+ abort();
+ }
+ return (status);
+}
diff --git a/usr/src/uts/common/krtld/reloc.h b/usr/src/uts/common/krtld/reloc.h
index 4f0c576203..c269cf41bd 100644
--- a/usr/src/uts/common/krtld/reloc.h
+++ b/usr/src/uts/common/krtld/reloc.h
@@ -256,7 +256,7 @@ extern const char *conv_reloc_SPARC_type(Word);
_kobj_printf(ops, MSG_REL_PREGEN, CONV_RELOC_TYPE((rtype))); \
_kobj_printf(ops, MSG_REL_FILE, (file)); \
_kobj_printf(ops, MSG_REL_SYM, ((sym) ? (sym) : MSG_STR_UNKNOWN)); \
- _kobj_printf(ops, MSG_REL_NONALIGN, EC_OFF((off)))
+ _kobj_printf(ops, MSG_REL_NONALIGN, (u_longlong_t)EC_OFF((off)))
#define REL_ERR_UNNOBITS(lml, file, sym, rtype, nbits) \
_kobj_printf(ops, MSG_REL_PREGEN, CONV_RELOC_TYPE((rtype))); \
@@ -268,9 +268,9 @@ extern const char *conv_reloc_SPARC_type(Word);
_kobj_printf(ops, MSG_REL_PREGEN, CONV_RELOC_TYPE((rtype))); \
_kobj_printf(ops, MSG_REL_FILE, (file)); \
_kobj_printf(ops, MSG_REL_SYM, ((sym) ? (sym) : MSG_STR_UNKNOWN)); \
- _kobj_printf(ops, MSG_REL_VALUE, EC_XWORD((uvalue))); \
+ _kobj_printf(ops, MSG_REL_VALUE, (u_longlong_t)EC_XWORD((uvalue))); \
_kobj_printf(ops, MSG_REL_LOSEBITS, (int)(nbits)); \
- _kobj_printf(ops, MSG_REL_OFFSET, EC_NATPTR((off)))
+ _kobj_printf(ops, MSG_REL_OFFSET, (u_longlong_t)EC_NATPTR((off)))
#define REL_ERR_NOFIT(lml, file, sym, rtype, uvalue) \
_kobj_printf(ops, MSG_REL_PREGEN, CONV_RELOC_TYPE((rtype))); \
diff --git a/usr/src/uts/common/sys/fm/protocol.h b/usr/src/uts/common/sys/fm/protocol.h
index 5eca760dad..e0140bb0fb 100644
--- a/usr/src/uts/common/sys/fm/protocol.h
+++ b/usr/src/uts/common/sys/fm/protocol.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _SYS_FM_PROTOCOL_H
@@ -200,6 +201,7 @@ extern "C" {
#define FM_FMRI_SCHEME_LEGACY "legacy-hc"
#define FM_FMRI_SCHEME_ZFS "zfs"
#define FM_FMRI_SCHEME_SW "sw"
+#define FM_FMRI_SCHEME_PATH "path"
/* Scheme versions */
#define FMD_SCHEME_VERSION0 0
@@ -225,6 +227,8 @@ extern "C" {
#define FM_ZFS_SCHEME_VERSION ZFS_SCHEME_VERSION0
#define SW_SCHEME_VERSION0 0
#define FM_SW_SCHEME_VERSION SW_SCHEME_VERSION0
+#define PATH_SCHEME_VERSION0 0
+#define FM_PATH_SCHEME_VERSION PATH_SCHEME_VERSION0
/* hc scheme member names */
#define FM_FMRI_HC_SERIAL_ID "serial"
@@ -328,6 +332,13 @@ extern "C" {
#define FM_FMRI_SW_CTXT_CTID "ctid"
#define FM_FMRI_SW_CTXT_STACK "stack"
+/* path scheme member names */
+#define FM_FMRI_PATH_VERSION "path-scheme-version"
+#define FM_FMRI_PATH "path"
+#define FM_FMRI_PATH_NAME "path-name"
+#define FM_FMRI_PATH_INST "path-instance"
+#define FM_FMRI_PATH_DIGRAPH_SCHEME "path-digraph-scheme"
+
extern nv_alloc_t *fm_nva_xcreate(char *, size_t);
extern void fm_nva_xdestroy(nv_alloc_t *);
diff --git a/usr/src/uts/i86pc/os/cpuid_subr.c b/usr/src/uts/i86pc/os/cpuid_subr.c
index c4230f6e07..6343051ef2 100644
--- a/usr/src/uts/i86pc/os/cpuid_subr.c
+++ b/usr/src/uts/i86pc/os/cpuid_subr.c
@@ -33,7 +33,7 @@
/*
* Copyright 2012 Jens Elkner <jel+illumos@cs.uni-magdeburg.de>
* Copyright 2012 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
- * Copyright 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -82,10 +82,13 @@
* 11 for family 0x16, models 00 - 0f
* 12 for family 0x16, models 30 - 3f
* 13 for family 0x17, models 00 - 0f
+ * 14 for family 0x17, models 10 - 1f
+ * 15 for family 0x17, models 30 - 3f
+ * 16 for family 0x17, models 70 - 7f
* Second index by (model & 0x3) for family 0fh,
* CPUID pkg bits (Fn8000_0001_EBX[31:28]) for later families.
*/
-static uint32_t amd_skts[14][8] = {
+static uint32_t amd_skts[17][8] = {
/*
* Family 0xf revisions B through E
*/
@@ -280,7 +283,7 @@ static uint32_t amd_skts[14][8] = {
},
/*
- * Family 0x17 models 00-0f
+ * Family 0x17 models 00-0f (Zen 1 - Naples, Ryzen)
*/
#define A_SKTS_13 13
{
@@ -294,6 +297,50 @@ static uint32_t amd_skts[14][8] = {
X86_SOCKET_SP3R2 /* 0b111 */
},
+ /*
+ * Family 0x17 models 10-1f (Zen 1 - APU: Raven Ridge)
+ */
+#define A_SKTS_14 14
+ {
+ X86_SOCKET_FP5, /* 0b000 */
+ X86_SOCKET_UNKNOWN, /* 0b001 */
+ X86_SOCKET_AM4, /* 0b010 */
+ X86_SOCKET_UNKNOWN, /* 0b011 */
+ X86_SOCKET_UNKNOWN, /* 0b100 */
+ X86_SOCKET_UNKNOWN, /* 0b101 */
+ X86_SOCKET_UNKNOWN, /* 0b110 */
+ X86_SOCKET_UNKNOWN /* 0b111 */
+ },
+
+ /*
+ * Family 0x17 models 30-3f (Zen 2 - Rome)
+ */
+#define A_SKTS_15 15
+ {
+ X86_SOCKET_UNKNOWN, /* 0b000 */
+ X86_SOCKET_UNKNOWN, /* 0b001 */
+ X86_SOCKET_UNKNOWN, /* 0b010 */
+ X86_SOCKET_UNKNOWN, /* 0b011 */
+ X86_SOCKET_SP3, /* 0b100 */
+ X86_SOCKET_UNKNOWN, /* 0b101 */
+ X86_SOCKET_UNKNOWN, /* 0b110 */
+ X86_SOCKET_SP3R2 /* 0b111 */
+ },
+
+ /*
+ * Family 0x17 models 70-7f (Zen 2 - Matisse)
+ */
+#define A_SKTS_16 16
+ {
+ X86_SOCKET_UNKNOWN, /* 0b000 */
+ X86_SOCKET_UNKNOWN, /* 0b001 */
+ X86_SOCKET_AM4, /* 0b010 */
+ X86_SOCKET_UNKNOWN, /* 0b011 */
+ X86_SOCKET_UNKNOWN, /* 0b100 */
+ X86_SOCKET_UNKNOWN, /* 0b101 */
+ X86_SOCKET_UNKNOWN, /* 0b110 */
+ X86_SOCKET_UNKNOWN /* 0b111 */
+ },
};
struct amd_sktmap_s {
@@ -332,6 +379,7 @@ static struct amd_sktmap_s amd_sktmap[X86_NUM_SOCKETS_AMD + 1] = {
{ X86_SOCKET_FT3B, "FT3b" },
{ X86_SOCKET_SP3, "SP3" },
{ X86_SOCKET_SP3R2, "SP3r2" },
+ { X86_SOCKET_FP5, "FP5" },
{ X86_SOCKET_UNKNOWN, "Unknown" }
};
@@ -487,6 +535,18 @@ static const struct amd_rev_mapent {
A_SKTS_13 },
{ 0x17, 0x01, 0x01, 0x1, 0x1, X86_CHIPREV_AMD_17_PiR_B2, "PiR-B2",
A_SKTS_13 },
+
+ { 0x17, 0x11, 0x11, 0x0, 0x0, X86_CHIPREV_AMD_17_RV_B0, "RV-B0",
+ A_SKTS_14 },
+ { 0x17, 0x11, 0x11, 0x1, 0x1, X86_CHIPREV_AMD_17_RV_B1, "RV-B1",
+ A_SKTS_14 },
+ { 0x17, 0x18, 0x18, 0x1, 0x1, X86_CHIPREV_AMD_17_PCO_B1, "PCO-B1",
+ A_SKTS_14 },
+
+ { 0x17, 0x30, 0x30, 0x0, 0x0, X86_CHIPREV_AMD_17_SSP_A0, "SSP-A0",
+ A_SKTS_15 },
+ { 0x17, 0x31, 0x31, 0x0, 0x0, X86_CHIPREV_AMD_17_SSP_B0, "SSP-B0",
+ A_SKTS_15 },
};
static void
diff --git a/usr/src/uts/intel/ia32/sys/Makefile b/usr/src/uts/intel/ia32/sys/Makefile
index 5f4708436f..0ef2320b16 100644
--- a/usr/src/uts/intel/ia32/sys/Makefile
+++ b/usr/src/uts/intel/ia32/sys/Makefile
@@ -19,17 +19,21 @@
# CDDL HEADER END
#
#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2020 Joyent, Inc.
+# uts/intel/ia32/sys/Makefile
#
# include global definitions
include ../../../../Makefile.master
HDRS= \
asm_linkage.h \
+ kdi_regs.h \
machtypes.h \
+ privmregs.h \
privregs.h \
psw.h \
pte.h \
diff --git a/usr/src/uts/intel/ia32/sys/kdi_regs.h b/usr/src/uts/intel/ia32/sys/kdi_regs.h
new file mode 100644
index 0000000000..e87948189a
--- /dev/null
+++ b/usr/src/uts/intel/ia32/sys/kdi_regs.h
@@ -0,0 +1,73 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#ifndef _IA32_SYS_KDI_REGS_H
+#define _IA32_SYS_KDI_REGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KDIREG_NGREG 21
+
+/*
+ * %ss appears in a different place than a typical struct regs, since the
+ * machine won't save %ss on a trap entry from the same privilege level.
+ */
+
+#define KDIREG_SAVFP 0
+#define KDIREG_SAVPC 1
+#define KDIREG_SS 2
+#define KDIREG_GS 3
+#define KDIREG_FS 4
+#define KDIREG_ES 5
+#define KDIREG_DS 6
+#define KDIREG_EDI 7
+#define KDIREG_ESI 8
+#define KDIREG_EBP 9
+#define KDIREG_ESP 10
+#define KDIREG_EBX 11
+#define KDIREG_EDX 12
+#define KDIREG_ECX 13
+#define KDIREG_EAX 14
+#define KDIREG_TRAPNO 15
+#define KDIREG_ERR 16
+#define KDIREG_EIP 17
+#define KDIREG_CS 18
+#define KDIREG_EFLAGS 19
+#define KDIREG_UESP 20
+
+#define KDIREG_PC KDIREG_EIP
+#define KDIREG_SP KDIREG_ESP
+#define KDIREG_FP KDIREG_EBP
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IA32_SYS_KDI_REGS_H */
diff --git a/usr/src/uts/intel/ia32/sys/privmregs.h b/usr/src/uts/intel/ia32/sys/privmregs.h
new file mode 100644
index 0000000000..87d9b74bfe
--- /dev/null
+++ b/usr/src/uts/intel/ia32/sys/privmregs.h
@@ -0,0 +1,67 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _IA32_SYS_PRIVMREGS_H
+#define _IA32_SYS_PRIVMREGS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__i386)
+#error "non-i386 code depends on i386 privileged header!"
+#endif
+
+#ifndef _ASM
+
+#define PM_GREGS (1 << 0)
+#define PM_CRREGS (1 << 1)
+#define PM_DRREGS (1 << 2)
+
+/*
+ * This structure is intended to represent a complete machine state for a CPU,
+ * when that information is available. It is only for use internally between
+ * KMDB and the kernel, or within MDB. Note that this isn't yet finished.
+ */
+typedef struct privmregs {
+ ulong_t pm_flags;
+ /* general registers */
+ struct regs pm_gregs;
+ /* cr0-8 */
+ ulong_t pm_cr[8];
+ /* dr0-8 */
+ ulong_t pm_dr[8];
+} privmregs_t;
+
+#endif /* !_ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_IA32_SYS_PRIVMREGS_H */
diff --git a/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c b/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c
index 11bddfa515..b1191f8f9e 100644
--- a/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c
+++ b/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c
@@ -75,9 +75,9 @@
* value in the corresponding data fabric's AMDF17_DF_CFG_ADDR_CTL.
*
* This means that we can map a northbridge to a data fabric device and a data
- * fabric device to a die. Because these are 1:1 mappings, there is a transitive
- * relationship and therefore we know which northbridge is associated with which
- * processor die. This is summarized in the following image:
+ * fabric device to a die. Because these are generally 1:1 mappings, there is a
+ * transitive relationship and therefore we know which northbridge is associated
+ * with which processor die. This is summarized in the following image:
*
* +-------+ +----------------------------+ +--------------+
* | Die 0 | ---> | Data Fabric PCI BDF 0/18/0 |-------> | Northbridge |
@@ -93,6 +93,17 @@
* of the data fabric accurately models hardware. All of the BDF values are in
* hex.
*
+ * Starting with the Rome generation of processors (Family 17h Model 30-3Fh),
+ * AMD has multiple northbridges that exist on a given die. All of these
+ * northbridges share the same data fabric and system management network port.
+ * From our perspective this means that some of the northbridge devices will be
+ * redundant and that we will no longer have a 1:1 mapping between the
+ * northbridge and the data fabric devices. Every data fabric will have a
+ * northbridge, but not every northbridge will have a data fabric device mapped.
+ * Because we're always trying to map from a die to a northbridge and not the
+ * reverse, the fact that there are extra northbridge devices hanging around
+ * that we don't know about shouldn't be a problem.
+ *
* -------------------------------
* Attach and Detach Complications
* -------------------------------
@@ -246,6 +257,20 @@ static const amdf17nbdf_table_t amdf17nbdf_dev_map[] = {
/* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */
{ 0x1450, AMD_NBDF_TYPE_NORTHBRIDGE },
{ 0x1460, AMD_NBDF_TYPE_DATA_FABRIC },
+ /* Family 17h Raven Ridge Models 10h-1fh (Zen uarch) */
+ { 0x15d0, AMD_NBDF_TYPE_NORTHBRIDGE },
+ { 0x15e8, AMD_NBDF_TYPE_DATA_FABRIC },
+ /* Family 17h Epyc Models 30h-3fh (Zen 2 uarch) */
+ { 0x1480, AMD_NBDF_TYPE_NORTHBRIDGE },
+ { 0x1490, AMD_NBDF_TYPE_DATA_FABRIC },
+ /*
+ * Family 17h Ryzen Models 70-7fh (Zen 2 uarch)
+ *
+ * While this family has its own PCI ID for the data fabric device, it
+ * shares the same northbridge ID as the Zen 2 EPYC models 30-3f --
+ * 0x1480.
+ */
+ { 0x1440, AMD_NBDF_TYPE_DATA_FABRIC },
{ PCI_EINVAL16 }
};
diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases
index fdcac2292b..8f4c2f1bf2 100644
--- a/usr/src/uts/intel/os/driver_aliases
+++ b/usr/src/uts/intel/os/driver_aliases
@@ -67,8 +67,13 @@ amd64_gart "pci1022,1103"
amd8111s "pci1022,7462"
amd_iommu "pci1002,5a23"
amd_iommu "pci1022,11ff"
+amdf17nbdf "pci1022,1440,p"
amdf17nbdf "pci1022,1450,p"
amdf17nbdf "pci1022,1460,p"
+amdf17nbdf "pci1022,1480,p"
+amdf17nbdf "pci1022,1490,p"
+amdf17nbdf "pci1022,15d0,p"
+amdf17nbdf "pci1022,15e8,p"
amdnbtemp "pci1022,1203,p"
amdnbtemp "pci1022,1303,p"
amdnbtemp "pci1022,1403,p"
diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h
index bbf8ed2c57..d0efa8c384 100644
--- a/usr/src/uts/intel/sys/x86_archext.h
+++ b/usr/src/uts/intel/sys/x86_archext.h
@@ -944,6 +944,21 @@ extern "C" {
#define X86_CHIPREV_AMD_17_PiR_B2 \
_X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0003)
+#define X86_CHIPREV_AMD_17_RV_B0 \
+ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0004)
+
+#define X86_CHIPREV_AMD_17_RV_B1 \
+ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0005)
+
+#define X86_CHIPREV_AMD_17_PCO_B1 \
+ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0006)
+
+#define X86_CHIPREV_AMD_17_SSP_A0 \
+ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0007)
+
+#define X86_CHIPREV_AMD_17_SSP_B0 \
+ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x17, 0x0008)
+
/*
* Various socket/package types, extended as the need to distinguish
* a new type arises. The top 8 byte identfies the vendor and the
@@ -998,7 +1013,8 @@ extern "C" {
#define X86_SOCKET_FT3B _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x1e)
#define X86_SOCKET_SP3 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x1f)
#define X86_SOCKET_SP3R2 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x20)
-#define X86_NUM_SOCKETS_AMD 0x21
+#define X86_SOCKET_FP5 _X86_SOCKET_MKVAL(X86_VENDOR_AMD, 0x21)
+#define X86_NUM_SOCKETS_AMD 0x22
/*
diff --git a/usr/src/uts/sparc/krtld/kobj_reloc.c b/usr/src/uts/sparc/krtld/kobj_reloc.c
index cc7df40d7c..2fea631bd1 100644
--- a/usr/src/uts/sparc/krtld/kobj_reloc.c
+++ b/usr/src/uts/sparc/krtld/kobj_reloc.c
@@ -207,7 +207,7 @@ do_relocate(struct module *mp, char *reltbl, int nreloc, int relocsize,
if ((rtype > R_SPARC_NUM) || IS_TLS_INS(rtype)) {
_kobj_printf(ops, "krtld: invalid relocation type %d",
rtype);
- _kobj_printf(ops, " at 0x%llx:", off);
+ _kobj_printf(ops, " at 0x%llx:", (u_longlong_t)off);
_kobj_printf(ops, " file=%s\n", mp->filename);
err = 1;
continue;
@@ -223,8 +223,8 @@ do_relocate(struct module *mp, char *reltbl, int nreloc, int relocsize,
(mp->symtbl+(stndx * mp->symhdr->sh_entsize));
_kobj_printf(ops, "krtld:\t%s",
conv_reloc_SPARC_type(rtype));
- _kobj_printf(ops, "\t0x%8llx", off);
- _kobj_printf(ops, " 0x%8llx", addend);
+ _kobj_printf(ops, "\t0x%8llx", (u_longlong_t)off);
+ _kobj_printf(ops, " 0x%8llx", (u_longlong_t)addend);
_kobj_printf(ops, " %s\n",
(const char *)mp->strings + symp->st_name);
}
@@ -309,8 +309,9 @@ do_relocate(struct module *mp, char *reltbl, int nreloc, int relocsize,
#ifdef KOBJ_DEBUG
if (kobj_debug & D_RELOCATIONS) {
- _kobj_printf(ops, "krtld:\t\t\t\t0x%8llx", off);
- _kobj_printf(ops, " 0x%8llx\n", value);
+ _kobj_printf(ops, "krtld:\t\t\t\t0x%8llx",
+ (u_longlong_t)off);
+ _kobj_printf(ops, " 0x%8llx\n", (u_longlong_t)value);
}
#endif
if (do_reloc_krtld(rtype, (unsigned char *)off, (Xword *)&value,
@@ -353,7 +354,7 @@ do_relocations(struct module *mp)
}
if (rshp->sh_info >= mp->hdr.e_shnum) {
_kobj_printf(ops, "do_relocations: %s ", mp->filename);
- _kobj_printf(ops, " sh_info out of range %lld\n", shn);
+ _kobj_printf(ops, " sh_info out of range %d\n", shn);
goto bad;
}
nreloc = rshp->sh_size / rshp->sh_entsize;