1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  */
15 
16 /*
17  * Chipset Enumeration
18  *
19  * Most x86 systems have some form of chipset which are components that exist on
20  * the motherboard that provide additional services that range from I/O such as
21  * memory and PCIe controllers (though as of this writing those mostly are a
22  * part of the CPU now) to additional functionality like Ethernet and USB
23  * controllers. At the moment, this module opts to enumerate a chipset node if
24  * there's something that exists under it that we care about such as:
25  *
26  *   o Temperature sensors
27  *   o Firmware modules
28  *
29  * If we do not detect anything, then we do not bother enumerating and trying to
30  * determine the different chipsets that are on the system. Currently, the only
31  * method for doing this is the presence of an Intel platform controller hub
32  * (PCH) temperature sensor.
33  */
34 
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <string.h>
39 
40 #include <sys/fm/protocol.h>
41 #include <fm/topo_mod.h>
42 #include <fm/topo_list.h>
43 #include <fm/topo_method.h>
44 
45 #include <topo_sensor.h>
46 
47 #define	CHIPSET_VERSION	1
48 
49 /*
50  * This is the path to the temperature sensor that, if present, indicates we
51  * should construct a chipset node.
52  */
53 static const char *topo_chipset_temp_sensor =
54 	"/dev/sensors/temperature/pch/ts.0";
55 
56 /*
57  * Attempt to determine if there is enough information for us to enumerate a
58  * chipset node, which usually means that we would enumerate something under it
59  * such as a temperature sensor or provide information about some piece of
60  * firmware that it has. Currently, if there is no temperature sensor, then we
61  * don't consider one to be present and don't do anything else.
62  */
63 static boolean_t
64 topo_chipset_present(void)
65 {
66 	struct stat st;
67 
68 	if (stat(topo_chipset_temp_sensor, &st) == 0 &&
69 	    S_ISCHR(st.st_mode)) {
70 		return (B_TRUE);
71 	}
72 
73 	return (B_FALSE);
74 }
75 
76 static int
77 topo_chipset_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
78     topo_instance_t min, topo_instance_t max, void *modarg, void *data)
79 {
80 	int ret;
81 	nvlist_t *fmri = NULL, *auth = NULL, *presource = NULL;
82 	tnode_t *tn = NULL;
83 	const topo_instance_t inst = 0;
84 
85 	topo_mod_dprintf(mod, "chipset_enum: asked to enumerate %s", name);
86 
87 	if (strcmp(name, CHIPSET) != 0) {
88 		topo_mod_dprintf(mod, "chipset_enum: asked to enumerate "
89 		    "unknown component");
90 		return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
91 	}
92 
93 	if (!topo_chipset_present()) {
94 		topo_mod_dprintf(mod, "chipset_enum: no device present", name);
95 		return (0);
96 	}
97 
98 	if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
99 		topo_mod_dprintf(mod, "chipset_enum: failed to get topo "
100 		    "auth: %s", topo_mod_errmsg(mod));
101 		/* topo_mod_auth() sets the module error */
102 		ret = -1;
103 		goto err;
104 	}
105 
106 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
107 	    CHIPSET, inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
108 		topo_mod_dprintf(mod, "chipset_enum: failed to get FMRI: %s",
109 		    topo_mod_errmsg(mod));
110 		/* topo_mod_hcfmri() sets the module error */
111 		ret = -1;
112 		goto err;
113 	}
114 
115 	if ((tn = topo_node_bind(mod, pnode, CHIPSET, inst, fmri)) == NULL) {
116 		topo_mod_dprintf(mod, "chipset_enum: failed to bind node: %s",
117 		    topo_mod_errmsg(mod));
118 		ret = -1;
119 		goto err;
120 	}
121 
122 	if (topo_node_resource(pnode, &presource, &ret) != 0) {
123 		topo_mod_dprintf(mod, "chipset_enum: failed to get parent "
124 		    "resource %s\n", topo_strerror(ret));
125 		ret = topo_mod_seterrno(mod, ret);
126 		goto err;
127 	}
128 
129 	if (topo_node_fru_set(tn, presource, 0, &ret) != 0) {
130 		topo_mod_dprintf(mod, "chipset_enum: failed to set FRU: %s",
131 		    topo_strerror(ret));
132 		ret = topo_mod_seterrno(mod, ret);
133 		goto err;
134 	}
135 
136 	/*
137 	 * Finally, create the temperature sensor.
138 	 */
139 	if ((ret = topo_sensor_create_scalar_sensor(mod, tn,
140 	    topo_chipset_temp_sensor, "temp")) != 0) {
141 		topo_mod_dprintf(mod, "failed to create chipset temperature "
142 		    "sensor");
143 		goto err;
144 	}
145 
146 	nvlist_free(auth);
147 	nvlist_free(fmri);
148 	nvlist_free(presource);
149 	return (0);
150 err:
151 	nvlist_free(auth);
152 	nvlist_free(fmri);
153 	nvlist_free(presource);
154 	topo_node_unbind(tn);
155 	return (ret);
156 }
157 
158 static const topo_modops_t chipset_ops = {
159 	topo_chipset_enum, NULL
160 };
161 
162 static topo_modinfo_t chipset_mod = {
163 	CHIPSET, FM_FMRI_SCHEME_HC, CHIPSET_VERSION, &chipset_ops
164 };
165 
166 int
167 _topo_init(topo_mod_t *mod, topo_version_t version)
168 {
169 	if (getenv("TOPOCHIPSETDEBUG") != NULL) {
170 		topo_mod_setdebug(mod);
171 	}
172 
173 	topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n",
174 	    CHIPSET);
175 
176 	if (version != -1) {
177 
178 	}
179 
180 	if (topo_mod_register(mod, &chipset_mod, TOPO_VERSION) != 0) {
181 		return (-1);
182 	}
183 
184 	return (0);
185 }
186 
187 void
188 _topo_fini(topo_mod_t *mod)
189 {
190 }
191