xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/pcie.c (revision 84ceaea936ebcf122d4f0756d298adf307fd491d)
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 2023 Oxide Computer Company
14  */
15 
16 /*
17  * This provides the basic mechanisms for dealing with the pcie schema. A pcie
18  * FMRI has the form:
19  *
20  *     pcie:///cpu=C[/root-complex=R[function=F[...]]]
21  *
22  * That is to say that the top level nodes represent physical CPUs in the
23  * system and their PCIe root complexes are enumerated directly under that.
24  *
25  * Each node inherits the FMRI of its parent, and then appends a new
26  * '/<type>=<instance>' part, for example:
27  *
28  *     pcie:///cpu=0
29  *     pcie:///cpu=0/root-complex=0
30  *     pcie:///cpu=0/root-complex=0/function=0
31  *     pcie:///cpu=0/root-complex=0/function=0/port=0
32  *
33  * Types used in the scheme are:
34  *	cpu
35  *	root-complex
36  *	device
37  *	function
38  *	port
39  *	link
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <stdbool.h>
47 #include <ctype.h>
48 #include <alloca.h>
49 #include <limits.h>
50 #include <fm/topo_mod.h>
51 #include <sys/param.h>
52 #include <sys/systeminfo.h>
53 #include <sys/fm/protocol.h>
54 #include <sys/stat.h>
55 
56 #include <topo_method.h>
57 #include <topo_subr.h>
58 #include <pthread.h>
59 
60 #include <pcie.h>
61 
62 static size_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)63 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
64 {
65 	size_t size = 0;
66 	uint8_t version;
67 	nvlist_t **plist;
68 	uint_t nplist;
69 	int err;
70 
71 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
72 	    version > FM_PCIE_SCHEME_VERSION) {
73 		return (0);
74 	}
75 
76 	if (!topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_PCIE, NULL,
77 	    ":///")) {
78 		return (0);
79 	}
80 
81 	err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_PCIE_LIST,
82 	    &plist, &nplist);
83 	if (err != 0 || plist == NULL)
84 		return (0);
85 
86 	for (uint_t i = 0; i < nplist; i++) {
87 		char *name = NULL;
88 		char *id = NULL;
89 
90 		if (i > 0) {
91 			if (!topo_fmristr_build(&size, buf, buflen, "/", NULL,
92 			    NULL)) {
93 				return (0);
94 			}
95 		}
96 		if (nvlist_lookup_string(plist[i],
97 		    FM_FMRI_PCIE_NAME, &name) != 0 ||
98 		    nvlist_lookup_string(plist[i],
99 		    FM_FMRI_PCIE_ID, &id) != 0) {
100 			return (0);
101 		}
102 		if (name == NULL || id == NULL)
103 			return (0);
104 		if (!topo_fmristr_build(&size, buf, buflen, "=", name, id))
105 			return (0);
106 	}
107 
108 	return (size);
109 }
110 
111 static int
pcie_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)112 pcie_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
113     nvlist_t *nvl, nvlist_t **out)
114 {
115 	size_t len;
116 	char *name = NULL;
117 	nvlist_t *fmristr;
118 
119 	if (version > TOPO_METH_NVL2STR_VERSION)
120 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
121 
122 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0)
123 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
124 
125 	if ((name = topo_mod_alloc(mod, len + 1)) == NULL)
126 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
127 
128 	if (fmri_nvl2str(nvl, name, len + 1) == 0) {
129 		topo_mod_free(mod, name, len + 1);
130 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
131 	}
132 
133 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
134 		topo_mod_free(mod, name, len + 1);
135 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
136 	}
137 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
138 		topo_mod_free(mod, name, len + 1);
139 		nvlist_free(fmristr);
140 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
141 	}
142 	topo_mod_free(mod, name, len + 1);
143 	*out = fmristr;
144 
145 	return (0);
146 }
147 
148 static nvlist_t *
fmri_create_err(topo_mod_t * mod,nvlist_t ** list,uint_t elems,nvlist_t * fmri,int err)149 fmri_create_err(topo_mod_t *mod, nvlist_t **list, uint_t elems,
150     nvlist_t *fmri, int err)
151 {
152 	if (list != NULL) {
153 		for (uint_t i = 0; i < elems; i++)
154 			nvlist_free(list[i]);
155 
156 		topo_mod_free(mod, list, sizeof (nvlist_t *) * elems);
157 	}
158 
159 	nvlist_free(fmri);
160 
161 	(void) topo_mod_seterrno(mod, err);
162 
163 	topo_mod_dprintf(mod, "unable to create pcie FMRI: %s\n",
164 	    topo_mod_errmsg(mod));
165 
166 	return (NULL);
167 }
168 
169 static nvlist_t *
fmri_create_component(topo_mod_t * mod,const char * name,topo_instance_t inst)170 fmri_create_component(topo_mod_t *mod, const char *name, topo_instance_t inst)
171 {
172 	char str[21]; /* decimal representation of UINT64_MAX + '\0' */
173 	nvlist_t *comp;
174 
175 	if (topo_mod_nvalloc(mod, &comp, NV_UNIQUE_NAME) != 0)
176 		return (NULL);
177 
178 	(void) snprintf(str, sizeof (str), "%" PRIu64, inst);
179 
180 	if (nvlist_add_string(comp, FM_FMRI_PCIE_NAME, name) != 0 ||
181 	    nvlist_add_string(comp, FM_FMRI_PCIE_ID, str) != 0) {
182 		nvlist_free(comp);
183 		return (NULL);
184 	}
185 
186 	return (comp);
187 }
188 
189 static nvlist_t *
fmri_create(topo_mod_t * mod,nvlist_t * pfmri,const char * name,topo_instance_t inst,nvlist_t * auth)190 fmri_create(topo_mod_t *mod, nvlist_t *pfmri, const char *name,
191     topo_instance_t inst, nvlist_t *auth)
192 {
193 	nvlist_t **pplist = NULL;
194 	nvlist_t **plist = NULL;
195 	nvlist_t *fmri = NULL;
196 	uint_t pelems = 0, elems;
197 	uint_t i;
198 
199 	/*
200 	 * This FMRI will be constructed from the FMRI of the parent, with a
201 	 * new path component (name=inst) after it. We copy the parent's
202 	 * property list to this node, and then add our new property to the
203 	 * end.
204 	 */
205 
206 	/* Retrieve the parent's property list */
207 	if (pfmri != NULL) {
208 		if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_PCIE_LIST,
209 		    &pplist, &pelems) != 0) {
210 			return (fmri_create_err(mod, plist, pelems, fmri,
211 			    EMOD_FMRI_MALFORM));
212 		}
213 	}
214 
215 	/* We want space for an extra entry in the new FMRI's property list */
216 	elems = pelems + 1;
217 
218 	plist = topo_mod_zalloc(mod, sizeof (nvlist_t *) * elems);
219 	if (plist == NULL) {
220 		return (fmri_create_err(mod, plist, elems, fmri,
221 		    ETOPO_FMRI_NOMEM));
222 	}
223 
224 	/* Copy the parent properties */
225 	for (i = 0; i < pelems; i++) {
226 		if (topo_mod_nvdup(mod, pplist[i], &plist[i]) != 0) {
227 			return (fmri_create_err(mod, plist, elems, fmri,
228 			    EMOD_FMRI_NVL));
229 		}
230 	}
231 
232 	/* Add the new path component */
233 	if ((plist[i] = fmri_create_component(mod, name, inst)) == NULL) {
234 		return (fmri_create_err(mod, plist, elems, fmri,
235 		    EMOD_FMRI_NVL));
236 	}
237 
238 	/* Create the fmri */
239 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
240 		return (fmri_create_err(mod, plist, elems, fmri,
241 		    EMOD_FMRI_NVL));
242 	}
243 
244 	if (nvlist_add_uint8(fmri, FM_VERSION, FM_PCIE_SCHEME_VERSION) ||
245 	    nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_PCIE)) {
246 		return (fmri_create_err(mod, plist, elems, fmri,
247 		    EMOD_FMRI_NVL));
248 	}
249 
250 	/* Add the new property list */
251 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_PCIE_LIST,
252 	    plist, elems) != 0) {
253 		return (fmri_create_err(mod, plist, elems, fmri,
254 		    EMOD_FMRI_NVL));
255 	}
256 
257 	if (auth != NULL)
258 		(void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, auth);
259 
260 	if (plist != NULL) {
261 		for (uint_t i = 0; i < elems; i++)
262 			nvlist_free(plist[i]);
263 		topo_mod_free(mod, plist, sizeof (nvlist_t *) * elems);
264 	}
265 
266 	return (fmri);
267 }
268 
269 static int
pcie_fmri_create_meth(topo_mod_t * mod,tnode_t * tnode,topo_version_t version,nvlist_t * in,nvlist_t ** out)270 pcie_fmri_create_meth(topo_mod_t *mod, tnode_t *tnode, topo_version_t version,
271     nvlist_t *in, nvlist_t **out)
272 {
273 	char *name;
274 	topo_instance_t inst;
275 	nvlist_t *args, *auth = NULL, *pfmri = NULL;
276 	int ret;
277 
278 	if (version > TOPO_METH_FMRI_VERSION)
279 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
280 
281 	if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0 ||
282 	    nvlist_lookup_uint64(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0) {
283 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
284 	}
285 
286 	if ((ret = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL,
287 	    &args)) != 0) {
288 		if (ret != ENOENT)
289 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
290 	} else {
291 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
292 		    &pfmri);
293 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH,
294 		    &auth);
295 	}
296 
297 	*out = fmri_create(mod, pfmri, name, inst, auth);
298 	if (*out == NULL)
299 		return (-1);
300 
301 	return (0);
302 }
303 
304 const topo_method_t pcie_methods[] = {
305 	{
306 		.tm_name = TOPO_METH_NVL2STR,
307 		.tm_desc = TOPO_METH_NVL2STR_DESC,
308 		.tm_version = TOPO_METH_NVL2STR_VERSION,
309 		.tm_stability = TOPO_STABILITY_INTERNAL,
310 		.tm_func = pcie_fmri_nvl2str
311 	},
312 	{
313 		.tm_name = TOPO_METH_FMRI,
314 		.tm_desc = TOPO_METH_FMRI_DESC,
315 		.tm_version = TOPO_METH_FMRI_VERSION,
316 		.tm_stability = TOPO_STABILITY_INTERNAL,
317 		.tm_func = pcie_fmri_create_meth
318 	},
319 	{ NULL }
320 };
321 
322 int
pcie_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * u1 __unused,void * u2 __unused)323 pcie_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
324     topo_instance_t min, topo_instance_t max, void *u1 __unused,
325     void *u2 __unused)
326 {
327 	topo_mod_dprintf(mod, "enumerating pcie: %s (%"PRIu64" - %"PRIu64")",
328 	    name, min, max);
329 
330 	(void) topo_method_register(mod, pnode, pcie_methods);
331 	return (0);
332 }
333 
334 static void
pcie_rele(topo_mod_t * mp,tnode_t * node)335 pcie_rele(topo_mod_t *mp, tnode_t *node)
336 {
337 	topo_method_unregister_all(mp, node);
338 }
339 
340 static const topo_modops_t pcie_ops = {
341 	.tmo_enum = pcie_enum,
342 	.tmo_release = pcie_rele
343 };
344 
345 static const topo_modinfo_t pcie_info = {
346 	.tmi_desc = PCIE,
347 	.tmi_scheme = FM_FMRI_SCHEME_PCIE,
348 	.tmi_version = PCIE_VERSION,
349 	.tmi_ops = &pcie_ops
350 };
351 
352 int
pcie_init(topo_mod_t * mod,topo_version_t version)353 pcie_init(topo_mod_t *mod, topo_version_t version)
354 {
355 	if (getenv("TOPOPCIEDEBUG"))
356 		topo_mod_setdebug(mod);
357 
358 	topo_mod_dprintf(mod, "initializing pcie builtin");
359 
360 	if (version != PCIE_VERSION)
361 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
362 
363 	if (topo_mod_register(mod, &pcie_info, TOPO_VERSION) != 0) {
364 		topo_mod_dprintf(mod, "failed to register pcie: %s",
365 		    topo_mod_errmsg(mod));
366 		return (-1); /* mod errno already set */
367 	}
368 
369 	return (0);
370 }
371 
372 void
pcie_fini(topo_mod_t * mod)373 pcie_fini(topo_mod_t *mod)
374 {
375 	topo_mod_unregister(mod);
376 }
377