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 #include <stdbool.h>
17 #include <string.h>
18
19 #include <sys/fm/protocol.h>
20 #include <fm/topo_mod.h>
21 #include <fm/topo_list.h>
22 #include <fm/topo_method.h>
23
24 #include "topo_pcie_impl.h"
25
26 const char *
pcie_type_name(pcie_node_type_t type)27 pcie_type_name(pcie_node_type_t type)
28 {
29 switch (type) {
30 case PCIE_NODE_ROOTNEXUS:
31 return ("root-complex");
32 case PCIE_NODE_ROOTPORT:
33 return ("root-port");
34 case PCIE_NODE_PCI_DEV:
35 return ("pci");
36 case PCIE_NODE_PCIE_DEV:
37 return ("pcie");
38 case PCIE_NODE_SWITCH_UP:
39 return ("upstream-switch");
40 case PCIE_NODE_SWITCH_DOWN:
41 return ("downstream-switch");
42 case PCIE_NODE_PCIE_PCI:
43 return ("pcie-pci-bridge");
44 case PCIE_NODE_PCI_PCIE:
45 return ("pci-pcie-bridge");
46 default:
47 return ("unhandled type name");
48 }
49 }
50
51 uint_t
pcie_speed2gen(int64_t speed)52 pcie_speed2gen(int64_t speed)
53 {
54 switch (speed) {
55 case 2500000000LL:
56 return (1);
57 case 5000000000LL:
58 return (2);
59 case 8000000000LL:
60 return (3);
61 case 16000000000LL:
62 return (4);
63 case 32000000000LL:
64 return (5);
65 default:
66 break;
67 }
68 return (0);
69 }
70
71 const char *
pcie_speed2str(int64_t speed)72 pcie_speed2str(int64_t speed)
73 {
74 switch (speed) {
75 case 2500000000LL:
76 return ("2.5");
77 case 5000000000LL:
78 return ("5.0");
79 case 8000000000LL:
80 return ("8.0");
81 case 16000000000LL:
82 return ("16.0");
83 case 32000000000LL:
84 return ("32.0");
85 default:
86 break;
87 }
88 return ("0");
89 }
90
91 bool
pcie_topo_pgroup_create(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg)92 pcie_topo_pgroup_create(topo_mod_t *mod, tnode_t *tn,
93 const topo_pgroup_info_t *pg)
94 {
95 int err;
96
97 if (topo_pgroup_create(tn, pg, &err) != 0) {
98 if (err == ETOPO_PROP_DEFD)
99 return (true);
100 topo_mod_dprintf(mod, "failed to create property group %s: %s",
101 pg->tpi_name, topo_strerror(err));
102 (void) topo_mod_seterrno(mod, err);
103 return (false);
104 }
105 return (true);
106 }
107
108 bool
pcie_topo_range_create(topo_mod_t * mod,tnode_t * tn,const char * name,topo_instance_t min,topo_instance_t max)109 pcie_topo_range_create(topo_mod_t *mod, tnode_t *tn, const char *name,
110 topo_instance_t min, topo_instance_t max)
111 {
112 int err;
113
114 err = topo_node_range_create(mod, tn, name, min, max);
115 if (err == EMOD_NODE_DUP)
116 return (true);
117 if (err != 0) {
118 topo_mod_dprintf(mod,
119 "Failed to create range for %s [%"PRIu64",%"PRIu64"]: %s",
120 name, min, max, topo_mod_errmsg(mod));
121 return (false);
122 }
123 return (true);
124 }
125
126 static bool
pcie_topo_prop_set(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,topo_type_t type,const char * name,void * val)127 pcie_topo_prop_set(topo_mod_t *mod, tnode_t *tn, const topo_pgroup_info_t *pg,
128 topo_type_t type, const char *name, void *val)
129 {
130 int ret, err;
131 /*
132 * All properties are flagged as immutable as they reflect read-only
133 * information derived from the device tree/device.
134 */
135 const int flag = TOPO_PROP_IMMUTABLE;
136
137 switch (type) {
138 case TOPO_TYPE_UINT32:
139 ret = topo_prop_set_uint32(tn, pg->tpi_name, name, flag,
140 *(uint32_t *)val, &err);
141 break;
142 case TOPO_TYPE_UINT64:
143 ret = topo_prop_set_uint64(tn, pg->tpi_name, name, flag,
144 *(uint64_t *)val, &err);
145 break;
146 case TOPO_TYPE_STRING:
147 ret = topo_prop_set_string(tn, pg->tpi_name, name, flag,
148 (char *)val, &err);
149 break;
150 default:
151 topo_mod_dprintf(mod, "%s - unhandled property type %u",
152 __func__, type);
153 return (false);
154 }
155
156 if (ret != 0) {
157 topo_mod_dprintf(mod, "failed to set property %s/%s: %s",
158 pg->tpi_name, name, topo_strerror(err));
159 (void) topo_mod_seterrno(mod, err);
160 return (false);
161 }
162
163 return (true);
164 }
165
166 bool
pcie_topo_prop_set32(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,const char * name,uint32_t val)167 pcie_topo_prop_set32(topo_mod_t *mod, tnode_t *tn, const topo_pgroup_info_t *pg,
168 const char *name, uint32_t val)
169 {
170 return (pcie_topo_prop_set(mod, tn, pg, TOPO_TYPE_UINT32, name,
171 (void *)&val));
172 }
173
174 bool
pcie_topo_prop_set64(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,const char * name,uint64_t val)175 pcie_topo_prop_set64(topo_mod_t *mod, tnode_t *tn, const topo_pgroup_info_t *pg,
176 const char *name, uint64_t val)
177 {
178 return (pcie_topo_prop_set(mod, tn, pg, TOPO_TYPE_UINT64, name,
179 (void *)&val));
180 }
181
182 bool
pcie_topo_prop_set32_array(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,const char * name,uint32_t * vals,int nval)183 pcie_topo_prop_set32_array(topo_mod_t *mod, tnode_t *tn,
184 const topo_pgroup_info_t *pg, const char *name, uint32_t *vals, int nval)
185 {
186 int ret, err;
187
188 ret = topo_prop_set_uint32_array(tn, pg->tpi_name, name, 0,
189 vals, nval, &err);
190 if (ret != 0) {
191 topo_mod_dprintf(mod, "failed to set property %s/%s: %s",
192 pg->tpi_name, name, topo_strerror(err));
193 (void) topo_mod_seterrno(mod, err);
194 return (false);
195 }
196
197 return (true);
198 }
199
200 bool
pcie_topo_prop_set64_array(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,const char * name,uint64_t * vals,int nval)201 pcie_topo_prop_set64_array(topo_mod_t *mod, tnode_t *tn,
202 const topo_pgroup_info_t *pg, const char *name, uint64_t *vals, int nval)
203 {
204 int ret, err;
205
206 ret = topo_prop_set_uint64_array(tn, pg->tpi_name, name, 0,
207 vals, nval, &err);
208 if (ret != 0) {
209 topo_mod_dprintf(mod, "failed to set property %s/%s: %s",
210 pg->tpi_name, name, topo_strerror(err));
211 (void) topo_mod_seterrno(mod, err);
212 return (false);
213 }
214
215 return (true);
216 }
217
218 bool
pcie_topo_prop_setstr(topo_mod_t * mod,tnode_t * tn,const topo_pgroup_info_t * pg,const char * name,const char * val)219 pcie_topo_prop_setstr(topo_mod_t *mod, tnode_t *tn,
220 const topo_pgroup_info_t *pg, const char *name, const char *val)
221 {
222 return (pcie_topo_prop_set(mod, tn, pg, TOPO_TYPE_STRING, name,
223 (void *)val));
224 }
225
226 int32_t
pcie_devinfo_get32(topo_mod_t * mod,di_node_t did,const char * name)227 pcie_devinfo_get32(topo_mod_t *mod, di_node_t did, const char *name)
228 {
229 int32_t *iprop;
230 int nprop;
231
232 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, did, name, &iprop);
233
234 if (nprop != 1) {
235 if (nprop != -1) {
236 topo_mod_dprintf(mod,
237 "devinfo property %s has %u value(s); skipping",
238 name, nprop);
239 }
240 return (-1);
241 }
242
243 return (iprop[0]);
244 }
245
246 int64_t
pcie_devinfo_get64(topo_mod_t * mod,di_node_t did,const char * name)247 pcie_devinfo_get64(topo_mod_t *mod, di_node_t did, const char *name)
248 {
249 int64_t *iprop;
250 int nprop;
251
252 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, did, name, &iprop);
253
254 if (nprop != 1) {
255 if (nprop != -1) {
256 topo_mod_dprintf(mod,
257 "devinfo property %s has %u value(s); skipping",
258 name, nprop);
259 }
260 return (-1);
261 }
262
263 return (iprop[0]);
264 }
265
266 bool
pcie_devinfo_getbool(topo_mod_t * mod,di_node_t did,const char * name)267 pcie_devinfo_getbool(topo_mod_t *mod, di_node_t did, const char *name)
268 {
269 di_prop_t prop;
270
271 prop = di_prop_find(DDI_DEV_T_ANY, did, name);
272
273 return (prop != DI_PROP_NIL &&
274 di_prop_type(prop) == DI_PROP_TYPE_BOOLEAN);
275 }
276
277 /*
278 * This is a convenience function to copy a property from a devinfo node to
279 * a topo node property group, with appropriate error checking and output if
280 * anything goes wrong.
281 * devinfo numeric properties are signed but we always do explicit conversion to
282 * unsigned when storing the values back to the topo node.
283 */
284 bool
pcie_topo_prop_copy(topo_mod_t * mod,di_node_t did,tnode_t * tn,const topo_pgroup_info_t * pg,topo_type_t type,const char * src,const char * dst)285 pcie_topo_prop_copy(topo_mod_t *mod, di_node_t did, tnode_t *tn,
286 const topo_pgroup_info_t *pg, topo_type_t type,
287 const char *src, const char *dst)
288 {
289 int32_t *iprop;
290 int64_t *lprop;
291 char *sprop;
292 int nprop;
293
294 switch (type) {
295 case TOPO_TYPE_UINT32:
296 case TOPO_TYPE_UINT32_ARRAY:
297 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, did, src, &iprop);
298 break;
299 case TOPO_TYPE_UINT64:
300 case TOPO_TYPE_UINT64_ARRAY:
301 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, did, src, &lprop);
302 break;
303 case TOPO_TYPE_STRING:
304 nprop = di_prop_lookup_strings(DDI_DEV_T_ANY, did, src, &sprop);
305 break;
306 default:
307 topo_mod_dprintf(mod, "%s - unhandled property type %u\n",
308 __func__, type);
309 return (false);
310 }
311
312 if (nprop < 1) {
313 if (nprop != -1) {
314 topo_mod_dprintf(mod,
315 "devinfo property %s has %u value(s); skipping",
316 src, nprop);
317 }
318 return (false);
319 }
320
321 switch (type) {
322 case TOPO_TYPE_UINT32:
323 return (pcie_topo_prop_set32(mod, tn, pg, dst, iprop[0]));
324 case TOPO_TYPE_UINT32_ARRAY:
325 return (pcie_topo_prop_set32_array(mod, tn, pg, dst,
326 (uint32_t *)iprop, nprop));
327 case TOPO_TYPE_UINT64:
328 return (pcie_topo_prop_set64(mod, tn, pg, dst, lprop[0]));
329 case TOPO_TYPE_UINT64_ARRAY:
330 return (pcie_topo_prop_set64_array(mod, tn, pg, dst,
331 (uint64_t *)lprop, nprop));
332 case TOPO_TYPE_STRING:
333 return (pcie_topo_prop_setstr(mod, tn, pg, dst, sprop));
334 default:
335 abort();
336 }
337
338 return (false);
339 }
340