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 2024 Oxide Computer Company
14 */
15
16 /*
17 * This file is focused upon building up the tree of information that we need to
18 * build the module's various topology nodes as well as any methods that need to
19 * operate on them.
20 */
21
22 #include <sys/fm/protocol.h>
23 #include <fm/topo_hc.h>
24 #include <sys/devfm.h>
25 #include <assert.h>
26
27 #include "topo_zen_impl.h"
28
29 static const topo_pgroup_info_t topo_zen_chip_pgroup = {
30 TOPO_PGROUP_CHIP,
31 TOPO_STABILITY_PRIVATE,
32 TOPO_STABILITY_PRIVATE,
33 1
34 };
35
36 static const topo_pgroup_info_t topo_zen_ccd_pgroup = {
37 TOPO_PGROUP_CCD,
38 TOPO_STABILITY_PRIVATE,
39 TOPO_STABILITY_PRIVATE,
40 1
41 };
42
43 static const topo_pgroup_info_t topo_zen_ccx_pgroup = {
44 TOPO_PGROUP_CCX,
45 TOPO_STABILITY_PRIVATE,
46 TOPO_STABILITY_PRIVATE,
47 1
48 };
49
50 static const topo_pgroup_info_t topo_zen_core_pgroup = {
51 TOPO_PGROUP_CORE,
52 TOPO_STABILITY_PRIVATE,
53 TOPO_STABILITY_PRIVATE,
54 1
55 };
56
57 static const topo_pgroup_info_t topo_zen_strand_pgroup = {
58 TOPO_PGROUP_STRAND,
59 TOPO_STABILITY_PRIVATE,
60 TOPO_STABILITY_PRIVATE,
61 1
62 };
63
64 static const topo_pgroup_info_t topo_zen_cache_pgroup = {
65 TOPO_PGROUP_CACHE,
66 TOPO_STABILITY_PRIVATE,
67 TOPO_STABILITY_PRIVATE,
68 1
69 };
70
71 /*
72 * Common interface to create a topo node in our socket and bind it to its
73 * parent. The following properties are commonly shared between all nodes in the
74 * chip:
75 *
76 * o The serial and revision are part of the FMRI. We don't have a good way to
77 * get the orderable OPN for this. The brand string doesn't feel appropriate
78 * for this use case.
79 * o The FRU is always set to the top-level chip.
80 * o We do not set the ASRU given that it will vary from device to device.
81 */
82 static tnode_t *
topo_zen_create_tn(topo_mod_t * mod,zen_topo_enum_sock_t * sock,tnode_t * pnode,topo_instance_t inst,const char * name)83 topo_zen_create_tn(topo_mod_t *mod, zen_topo_enum_sock_t *sock, tnode_t *pnode,
84 topo_instance_t inst, const char *name)
85 {
86 int ret, err;
87 tnode_t *tn = NULL;
88 nvlist_t *fmri = NULL, *auth = NULL;
89
90 auth = topo_mod_auth(mod, pnode);
91 if (auth == NULL) {
92 topo_mod_dprintf(mod, "failed to get auth for %s[%" PRIu64 "]: "
93 "%s", name, inst, topo_mod_errmsg(mod));
94 return (NULL);
95 }
96
97 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, inst,
98 NULL, auth, NULL, sock->ztes_cpu_rev, sock->ztes_cpu_serial);
99 if (fmri == NULL) {
100 topo_mod_dprintf(mod, "failed to create FMRI for %s[%" PRIu64
101 "]: %s", name, inst, topo_mod_errmsg(mod));
102 nvlist_free(auth);
103 return (NULL);
104 }
105
106 tn = topo_node_bind(mod, pnode, name, inst, fmri);
107 nvlist_free(auth);
108 if (tn == NULL) {
109 topo_mod_dprintf(mod, "failed to bind node %s[%" PRIu64 "]: %s",
110 name, inst, topo_mod_errmsg(mod));
111 nvlist_free(fmri);
112 return (NULL);
113 }
114
115 if (sock->ztes_tn == NULL) {
116 ret = topo_node_fru_set(tn, fmri, 0, &err);
117 } else {
118 ret = topo_node_fru_set(tn, NULL, 0, &err);
119 }
120 nvlist_free(fmri);
121
122 if (ret != 0) {
123 topo_mod_dprintf(mod, "failed to set FRU for %s[%" PRIu64 "]: "
124 "%s", name, inst, topo_strerror(err));
125 (void) topo_mod_seterrno(mod, err);
126 topo_node_unbind(tn);
127 return (NULL);
128 }
129
130 return (tn);
131 }
132
133 static tnode_t *
topo_zen_build_cache(topo_mod_t * mod,zen_topo_enum_sock_t * sock,tnode_t * pnode,topo_instance_t inst,nvlist_t * nvl)134 topo_zen_build_cache(topo_mod_t *mod, zen_topo_enum_sock_t *sock,
135 tnode_t *pnode, topo_instance_t inst, nvlist_t *nvl)
136 {
137 int err;
138 uint32_t level, type, ways, line;
139 uint64_t sets, size, id;
140 const char *types[2];
141 const char *flags[2];
142 uint_t ntypes = 0, nflags = 0;
143
144 tnode_t *tn = topo_zen_create_tn(mod, sock, pnode, inst, CACHE);
145 if (tn == NULL) {
146 return (NULL);
147 }
148
149 if (nvlist_lookup_pairs(nvl, 0,
150 FM_CACHE_INFO_LEVEL, DATA_TYPE_UINT32, &level,
151 FM_CACHE_INFO_TYPE, DATA_TYPE_UINT32, &type,
152 FM_CACHE_INFO_NWAYS, DATA_TYPE_UINT32, &ways,
153 FM_CACHE_INFO_LINE_SIZE, DATA_TYPE_UINT32, &line,
154 FM_CACHE_INFO_NSETS, DATA_TYPE_UINT64, &sets,
155 FM_CACHE_INFO_TOTAL_SIZE, DATA_TYPE_UINT64, &size,
156 FM_CACHE_INFO_ID, DATA_TYPE_UINT64, &id, NULL) != 0) {
157 topo_mod_dprintf(mod, "internal cache nvlist missing expected "
158 "keys");
159 goto err;
160 }
161
162 if ((type & FM_CACHE_INFO_T_DATA) != 0) {
163 types[ntypes] = TOPO_PGROUP_CACHE_TYPES_DATA;
164 ntypes++;
165 }
166
167 if ((type & FM_CACHE_INFO_T_INSTR) != 0) {
168 types[ntypes] = TOPO_PGROUP_CACHE_TYPES_INSTR;
169 ntypes++;
170 }
171
172 if ((type & FM_CACHE_INFO_T_UNIFIED) != 0) {
173 flags[nflags] = TOPO_PGROUP_CACHE_FLAGS_UNIFIED;
174 nflags++;
175 }
176
177 if (nvlist_lookup_boolean(nvl, FM_CACHE_INFO_FULLY_ASSOC) == 0) {
178 flags[nflags] = TOPO_PGROUP_CACHE_FLAGS_FA;
179 nflags++;
180 }
181
182 assert(ntypes > 0);
183 if (topo_create_props(mod, tn, TOPO_PROP_IMMUTABLE,
184 &topo_zen_cache_pgroup,
185 TOPO_PGROUP_CACHE_LEVEL, TOPO_TYPE_UINT32, level,
186 TOPO_PGROUP_CACHE_WAYS, TOPO_TYPE_UINT32, ways,
187 TOPO_PGROUP_CACHE_SETS, TOPO_TYPE_UINT64, sets,
188 TOPO_PGROUP_CACHE_LINE_SIZE, TOPO_TYPE_UINT32, line,
189 TOPO_PGROUP_CACHE_SYSTEM_ID, TOPO_TYPE_UINT64, id,
190 TOPO_PGROUP_CACHE_SIZE, TOPO_TYPE_UINT64, size,
191 TOPO_PGROUP_CACHE_TYPES, TOPO_TYPE_STRING_ARRAY, types, ntypes,
192 NULL) != 0) {
193 goto err;
194 }
195
196 if (nflags > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_CACHE,
197 TOPO_PGROUP_CACHE_FLAGS, TOPO_PROP_IMMUTABLE, flags, nflags,
198 &err) != 0) {
199 topo_mod_dprintf(mod, "failed to create %s property: %s",
200 TOPO_PGROUP_CACHE_FLAGS, topo_strerror(err));
201 (void) topo_mod_seterrno(mod, err);
202 goto err;
203 }
204
205 return (tn);
206
207 err:
208 topo_node_unbind(tn);
209 return (NULL);
210
211 }
212
213 /*
214 * Build up an FMRI for the CPU scheme for this thread and set that as our ASRU
215 * for the thread.
216 */
217 static int
topo_zen_build_strand_asru(topo_mod_t * mod,zen_topo_enum_sock_t * sock,tnode_t * tn,uint32_t cpuid)218 topo_zen_build_strand_asru(topo_mod_t *mod, zen_topo_enum_sock_t *sock,
219 tnode_t *tn, uint32_t cpuid)
220 {
221 int err, ret;
222 nvlist_t *fmri;
223
224 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
225 return (topo_mod_seterrno(mod, EMOD_NOMEM));
226 }
227
228 if (nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION) != 0 ||
229 nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU) != 0 ||
230 nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpuid) != 0 ||
231 (sock->ztes_cpu_serial != NULL && nvlist_add_string(fmri,
232 FM_FMRI_CPU_SERIAL_ID, sock->ztes_cpu_serial) != 0)) {
233 topo_mod_dprintf(mod, "failed to construct CPU FMRI\n");
234 nvlist_free(fmri);
235 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
236 }
237
238 ret = topo_node_asru_set(tn, fmri, 0, &err);
239 nvlist_free(fmri);
240 if (ret != 0) {
241 topo_mod_dprintf(mod, "failed to set ASRU for thread: %s\n",
242 topo_strerror(err));
243 return (topo_mod_seterrno(mod, err));
244 }
245
246 return (0);
247 }
248
249 static int
topo_zen_build_strand(topo_mod_t * mod,zen_topo_enum_sock_t * sock,const amdzen_topo_core_t * core,zen_topo_enum_core_t * zt_core,uint32_t tid)250 topo_zen_build_strand(topo_mod_t *mod, zen_topo_enum_sock_t *sock,
251 const amdzen_topo_core_t *core, zen_topo_enum_core_t *zt_core, uint32_t tid)
252 {
253 uint32_t cpuid;
254 tnode_t *tn;
255
256 tn = topo_zen_create_tn(mod, sock, zt_core->ztcore_tn, tid, STRAND);
257 if (tn == NULL) {
258 return (-1);
259 }
260
261 /*
262 * Strands (hardware threads) have an ASRU that relates to their logical
263 * CPU. Set that up now. We currently only opt to set it on the strand
264 * because if we want to offline the core, it seems like that needs
265 * better semantics and perhaps wants a better way to indicate that in
266 * the scheme.
267 */
268 if (nvlist_lookup_pairs(zt_core->ztcore_nvls[tid], 0,
269 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32,
270 &cpuid, NULL) != 0) {
271 topo_mod_dprintf(mod, "internal thread %u nvlist "
272 "missing expected keys", tid);
273 topo_node_unbind(tn);
274 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
275 }
276
277 if (topo_zen_build_strand_asru(mod, sock, tn, cpuid) != 0) {
278 topo_node_unbind(tn);
279 return (-1);
280 }
281
282 if (topo_create_props(mod, tn, TOPO_PROP_IMMUTABLE,
283 &topo_zen_strand_pgroup,
284 TOPO_PGROUP_STRAND_CPUID, TOPO_TYPE_UINT32, cpuid,
285 TOPO_PGROUP_STRAND_APICID, TOPO_TYPE_UINT32,
286 core->atcore_apicids[tid], NULL) != 0) {
287 topo_mod_dprintf(mod, "failed to set strand properties\n");
288 topo_node_unbind(tn);
289 return (-1);
290 }
291
292 zt_core->ztcore_thr_tn[tid] = tn;
293 return (0);
294 }
295
296 static int
topo_zen_build_core(topo_mod_t * mod,zen_topo_enum_sock_t * sock,tnode_t * ccx_tn,const amdzen_topo_core_t * core,zen_topo_enum_core_t * zt_core)297 topo_zen_build_core(topo_mod_t *mod, zen_topo_enum_sock_t *sock,
298 tnode_t *ccx_tn, const amdzen_topo_core_t *core,
299 zen_topo_enum_core_t *zt_core)
300 {
301 zt_core->ztcore_tn = topo_zen_create_tn(mod, sock, ccx_tn,
302 core->atcore_phys_no, CORE);
303 if (zt_core->ztcore_tn == NULL) {
304 return (-1);
305 }
306
307 if (topo_create_props(mod, zt_core->ztcore_tn, TOPO_PROP_IMMUTABLE,
308 &topo_zen_core_pgroup,
309 TOPO_PGROUP_CORE_LOGID, TOPO_TYPE_UINT32, core->atcore_log_no,
310 TOPO_PGROUP_CORE_PHYSID, TOPO_TYPE_UINT32, core->atcore_phys_no,
311 NULL) != 0) {
312 return (-1);
313 }
314
315 if (topo_node_range_create(mod, zt_core->ztcore_tn, CACHE, 0, 2) != 0) {
316 topo_mod_dprintf(mod, "failed to create cache range: %s\n",
317 topo_mod_errmsg(mod));
318 return (-1);
319 }
320
321 if (zt_core->ztcore_l2 != NULL) {
322 zt_core->ztcore_l2_tn = topo_zen_build_cache(mod, sock,
323 zt_core->ztcore_tn, 2, zt_core->ztcore_l2);
324 if (zt_core->ztcore_l2_tn == NULL) {
325 return (-1);
326 }
327 }
328
329 if (zt_core->ztcore_l1i != NULL) {
330 zt_core->ztcore_l1i_tn = topo_zen_build_cache(mod, sock,
331 zt_core->ztcore_tn, 1, zt_core->ztcore_l1i);
332 if (zt_core->ztcore_l1i_tn == NULL) {
333 return (-1);
334 }
335 }
336
337 if (zt_core->ztcore_l1d != NULL) {
338 zt_core->ztcore_l1d_tn = topo_zen_build_cache(mod, sock,
339 zt_core->ztcore_tn, 0, zt_core->ztcore_l1d);
340 if (zt_core->ztcore_l1d_tn == NULL) {
341 return (-1);
342 }
343 }
344
345 if (topo_node_range_create(mod, zt_core->ztcore_tn, STRAND, 0,
346 core->atcore_nthreads - 1) != 0) {
347 topo_mod_dprintf(mod, "failed to create strand range: %s\n",
348 topo_mod_errmsg(mod));
349 return (-1);
350 }
351
352 for (uint32_t tid = 0; tid < core->atcore_nthreads; tid++) {
353 int ret;
354
355 if (core->atcore_thr_en[tid] == 0) {
356 continue;
357 }
358
359 if ((ret = topo_zen_build_strand(mod, sock, core, zt_core,
360 tid)) != 0) {
361 return (ret);
362 }
363 }
364
365 return (0);
366 }
367
368 static int
topo_zen_build_ccx(topo_mod_t * mod,zen_topo_enum_sock_t * sock,tnode_t * ccd_tn,const amdzen_topo_ccx_t * ccx,zen_topo_enum_ccx_t * zt_ccx)369 topo_zen_build_ccx(topo_mod_t *mod, zen_topo_enum_sock_t *sock, tnode_t *ccd_tn,
370 const amdzen_topo_ccx_t *ccx, zen_topo_enum_ccx_t *zt_ccx)
371 {
372 zt_ccx->ztccx_tn = topo_zen_create_tn(mod, sock, ccd_tn,
373 ccx->atccx_phys_no, CCX);
374 if (zt_ccx->ztccx_tn == NULL) {
375 return (-1);
376 }
377
378 if (topo_create_props(mod, zt_ccx->ztccx_tn, TOPO_PROP_IMMUTABLE,
379 &topo_zen_ccx_pgroup,
380 TOPO_PGROUP_CCX_LOGID, TOPO_TYPE_UINT32, ccx->atccx_log_no,
381 TOPO_PGROUP_CCX_PHYSID, TOPO_TYPE_UINT32, ccx->atccx_phys_no,
382 NULL) != 0) {
383 topo_node_unbind(zt_ccx->ztccx_tn);
384 zt_ccx->ztccx_tn = NULL;
385 return (-1);
386 }
387
388 if (topo_node_range_create(mod, zt_ccx->ztccx_tn, CACHE, 0, 0) != 0) {
389 topo_mod_dprintf(mod, "failed to create cache range: %s\n",
390 topo_mod_errmsg(mod));
391 topo_node_unbind(zt_ccx->ztccx_tn);
392 zt_ccx->ztccx_tn = NULL;
393 return (-1);
394 }
395
396 if (zt_ccx->ztccx_l3 != NULL) {
397 zt_ccx->ztccx_l3_tn = topo_zen_build_cache(mod, sock,
398 zt_ccx->ztccx_tn, 0, zt_ccx->ztccx_l3);
399 if (zt_ccx->ztccx_l3_tn == NULL) {
400 return (-1);
401 }
402 }
403
404 if (topo_node_range_create(mod, zt_ccx->ztccx_tn, CORE, 0,
405 ccx->atccx_nphys_cores - 1) != 0) {
406 topo_mod_dprintf(mod, "failed to create cores range: %s\n",
407 topo_mod_errmsg(mod));
408 return (-1);
409 }
410
411 for (uint32_t coreno = 0; coreno < ccx->atccx_nphys_cores; coreno++) {
412 int ret;
413
414 if (ccx->atccx_core_en[coreno] == 0) {
415 topo_mod_dprintf(mod, "skipping core %u\n", coreno);
416 continue;
417 }
418
419 if ((ret = topo_zen_build_core(mod, sock, zt_ccx->ztccx_tn,
420 &ccx->atccx_cores[coreno], &zt_ccx->ztccx_core[coreno])) !=
421 0) {
422 return (ret);
423 }
424 }
425
426 return (0);
427 }
428
429 static int
topo_zen_build_ccds(topo_mod_t * mod,zen_topo_enum_sock_t * sock)430 topo_zen_build_ccds(topo_mod_t *mod, zen_topo_enum_sock_t *sock)
431 {
432 tnode_t *chip = sock->ztes_tn;
433
434 if (topo_node_range_create(mod, chip, CCD, 0, sock->ztes_nccd - 1) !=
435 0) {
436 topo_mod_dprintf(mod, "failed to create CCD range: %s\n",
437 topo_mod_errmsg(mod));
438 return (-1);
439 }
440
441 for (uint32_t ccdno = 0; ccdno < sock->ztes_nccd; ccdno++) {
442 const amdzen_topo_ccd_t *ccd = &sock->ztes_ccd[ccdno];
443 zen_topo_enum_ccd_t *zt_ccd = &sock->ztes_tn_ccd[ccdno];
444
445 /*
446 * Make sure we skip any CCDs that don't actually exist.
447 */
448 if (ccd->atccd_err != AMDZEN_TOPO_CCD_E_OK) {
449 continue;
450 }
451
452 zt_ccd->ztccd_tn = topo_zen_create_tn(mod, sock, chip, ccdno,
453 CCD);
454 if (zt_ccd->ztccd_tn == NULL) {
455 return (-1);
456 }
457
458 if (topo_create_props(mod, zt_ccd->ztccd_tn,
459 TOPO_PROP_IMMUTABLE, &topo_zen_ccd_pgroup,
460 TOPO_PGROUP_CCD_LOGID, TOPO_TYPE_UINT32, ccd->atccd_log_no,
461 TOPO_PGROUP_CCD_PHYSID, TOPO_TYPE_UINT32,
462 ccd->atccd_phys_no, NULL) != 0) {
463 topo_node_unbind(zt_ccd->ztccd_tn);
464 zt_ccd->ztccd_tn = NULL;
465 return (-1);
466 }
467
468 /*
469 * At this point we should go create any additional sensors
470 * (such as the per-CCD Tctl) and probably set some methods,
471 * etc.
472 */
473
474 if (topo_node_range_create(mod, zt_ccd->ztccd_tn, CCX, 0,
475 ccd->atccd_nphys_ccx) != 0) {
476 topo_mod_dprintf(mod, "failed to create CCD range: "
477 "%s\n", topo_mod_errmsg(mod));
478 return (-1);
479 }
480
481 for (uint32_t ccxno = 0; ccxno < ccd->atccd_nphys_ccx;
482 ccxno++) {
483 int ret;
484
485 if (ccd->atccd_ccx_en[ccxno] == 0) {
486 continue;
487 }
488
489 if ((ret = topo_zen_build_ccx(mod, sock,
490 zt_ccd->ztccd_tn, &ccd->atccd_ccx[ccxno],
491 &zt_ccd->ztccd_ccx[ccxno])) != 0) {
492 return (ret);
493 }
494 }
495 }
496
497 return (0);
498 }
499
500 int
topo_zen_build_chip(topo_mod_t * mod,tnode_t * pnode,topo_instance_t inst,zen_topo_enum_sock_t * sock)501 topo_zen_build_chip(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
502 zen_topo_enum_sock_t *sock)
503 {
504 int ret;
505 tnode_t *chip;
506
507 chip = topo_zen_create_tn(mod, sock, pnode, inst, CHIP);
508 if (chip == NULL) {
509 return (-1);
510 }
511
512 if (topo_create_props(mod, chip, TOPO_PROP_IMMUTABLE,
513 &topo_zen_chip_pgroup,
514 TOPO_PGROUP_CHIP_BRAND, TOPO_TYPE_STRING, sock->ztes_cpu_brand,
515 TOPO_PGROUP_CHIP_FAMILY, TOPO_TYPE_INT32, sock->ztes_cpu_fam,
516 TOPO_PGROUP_CHIP_MODEL, TOPO_TYPE_INT32, sock->ztes_cpu_model,
517 TOPO_PGROUP_CHIP_STEPPING, TOPO_TYPE_INT32, sock->ztes_cpu_step,
518 TOPO_PGROUP_CHIP_SOCKET, TOPO_TYPE_STRING, sock->ztes_cpu_sock,
519 NULL) != 0) {
520 topo_node_unbind(chip);
521 return (-1);
522 }
523
524 if (sock->ztes_cpu_rev != NULL && topo_create_props(mod, chip,
525 TOPO_PROP_IMMUTABLE, &topo_zen_chip_pgroup,
526 TOPO_PGROUP_CHIP_REVISION, TOPO_TYPE_STRING, sock->ztes_cpu_rev,
527 NULL) != 0) {
528 topo_node_unbind(chip);
529 return (-1);
530 }
531
532 sock->ztes_tn = chip;
533 ret = topo_zen_build_ccds(mod, sock);
534
535 /*
536 * At this point we should flesh out the I/O die and all the UMCs, IOMS
537 * instances, and related. we would put the general thermal sensor that
538 * smntemp exposes as procnode.%u under the I/O die when we have it.
539 */
540
541 return (ret);
542 }
543