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 */
53static 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 */
63static boolean_t
64topo_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
76static int
77topo_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_temp_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);
150err:
151	nvlist_free(auth);
152	nvlist_free(fmri);
153	nvlist_free(presource);
154	topo_node_unbind(tn);
155	return (ret);
156}
157
158static const topo_modops_t chipset_ops = {
159	topo_chipset_enum, NULL
160};
161
162static topo_modinfo_t chipset_mod = {
163	CHIPSET, FM_FMRI_SCHEME_HC, CHIPSET_VERSION, &chipset_ops
164};
165
166int
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
187void
188_topo_fini(topo_mod_t *mod)
189{
190}
191