1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <string.h>
28#include <umem.h>
29#include <sys/mdesc.h>
30#include <sys/fm/ldom.h>
31
32#include <cpu_mdesc.h>
33
34static void *
35cpu_alloc(size_t size)
36{
37	return (umem_alloc(size, UMEM_DEFAULT));
38}
39
40static void
41cpu_free(void *data, size_t size)
42{
43	umem_free(data, size);
44}
45
46md_proc_t *
47cpu_find_proc(md_info_t *chip, uint32_t procid) {
48	int i;
49	md_proc_t *procp;
50
51	/* search the processor based on the physical id */
52	for (i = 0, procp = chip->procs; i < chip->nprocs; i++, procp++) {
53		if (procp->serialno != 0 && procid == procp->id) {
54			return (procp);
55		}
56	}
57
58	return (NULL);
59}
60
61md_cpumap_t *
62cpu_find_cpumap(md_info_t *chip, uint32_t cpuid) {
63	int i;
64	md_cpumap_t *mcmp;
65
66	for (i = 0, mcmp = chip->cpus; i < chip->ncpus; i++, mcmp++) {
67		if (cpuid == mcmp->cpumap_pid) {
68			return (mcmp);
69		}
70	}
71	return (NULL);
72}
73
74int
75cpu_get_serialid_mdesc(md_info_t *chip, uint32_t cpuid, uint64_t *serialidp)
76{
77	md_cpumap_t *mcmp;
78	if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) {
79		*serialidp = mcmp->cpumap_serialno;
80		return (0);
81	}
82	return (-1);
83}
84
85static int
86cpu_n1_mdesc_init(topo_mod_t *mod, md_t *mdp, md_info_t *chip)
87{
88	mde_cookie_t *listp;
89	md_cpumap_t *mcmp;
90	int i, num_nodes, idx;
91	uint64_t x;
92
93	num_nodes = md_node_count(mdp);
94	listp = topo_mod_zalloc(mod, sizeof (mde_cookie_t) * num_nodes);
95
96	chip->ncpus = md_scan_dag(mdp,
97	    MDE_INVAL_ELEM_COOKIE,
98	    md_find_name(mdp, "cpu"),
99	    md_find_name(mdp, "fwd"),
100	    listp);
101	topo_mod_dprintf(mod, "Found %d cpus\n", chip->ncpus);
102
103	chip->cpus = topo_mod_zalloc(mod, chip->ncpus * sizeof (md_cpumap_t));
104	chip->nprocs = chip->ncpus;
105	chip->procs = topo_mod_zalloc(mod, chip->nprocs * sizeof (md_proc_t));
106
107	for (idx = 0, mcmp = chip->cpus; idx < chip->ncpus; idx++, mcmp++) {
108
109		if (md_get_prop_val(mdp, listp[idx], MD_STR_ID, &x) < 0)
110			x = (uint64_t)-1; /* invalid value */
111		mcmp->cpumap_id = x;
112
113		if (md_get_prop_val(mdp, listp[idx], MD_STR_PID, &x) < 0)
114			x = mcmp->cpumap_id;
115		mcmp->cpumap_pid = x;
116
117		mcmp->cpumap_serialno = 0;
118		mcmp->cpumap_chipidx = -1;
119		if (md_get_prop_val(mdp, listp[idx], MD_STR_CPU_SERIAL,
120		    &mcmp->cpumap_serialno) < 0) {
121			continue;
122		}
123		if (mcmp->cpumap_serialno == 0) {
124			continue;
125		}
126
127		/*
128		 * This PRI/MD has no indentity info. of the FRU and no
129		 * physical proc id.
130		 * Find if there is already an existing processor entry
131		 * Assign procid based on the order found during reading
132		 */
133		for (i = 0; i < chip->nprocs &&
134		    chip->procs[i].serialno != 0; i++) {
135			if (mcmp->cpumap_serialno == chip->procs[i].serialno) {
136				break;
137			}
138		}
139		if (i < chip->nprocs) {
140			mcmp->cpumap_chipidx = i;
141			if (chip->procs[i].serialno == 0) {
142				chip->procs[i].id = i;
143				chip->procs[i].serialno = mcmp->cpumap_serialno;
144				topo_mod_dprintf(mod,
145				    "chip[%d] serial is %llx\n",
146				    i, chip->procs[i].serialno);
147			}
148		}
149
150	}
151
152	topo_mod_free(mod, listp, sizeof (mde_cookie_t) * num_nodes);
153
154	return (0);
155}
156
157static int
158cpu_n2_mdesc_init(topo_mod_t *mod, md_t *mdp, md_info_t *chip)
159{
160	mde_cookie_t *list1p, *list2p;
161	md_cpumap_t *mcmp;
162	md_proc_t *procp;
163	md_fru_t *frup;
164	int i, j, cnt;
165	int procid_flag = 0;
166	int nnode, ncomp, nproc, ncpu;
167	char *str = NULL;
168	uint64_t x, sn;
169	char *strserial, *end;
170
171	nnode = md_node_count(mdp);
172	list1p = topo_mod_zalloc(mod, sizeof (mde_cookie_t) * nnode);
173
174	/* Count the number of processors and strands */
175	ncomp = md_scan_dag(mdp,
176	    MDE_INVAL_ELEM_COOKIE,
177	    md_find_name(mdp, MD_STR_COMPONENT),
178	    md_find_name(mdp, "fwd"),
179	    list1p);
180	if (ncomp <= 0) {
181		topo_mod_dprintf(mod, "Component nodes not found\n");
182		topo_mod_free(mod, list1p, sizeof (mde_cookie_t) * nnode);
183		return (-1);
184	}
185	for (i = 0, nproc = 0, ncpu = 0; i < ncomp; i++) {
186		if (md_get_prop_str(mdp, list1p[i], MD_STR_TYPE, &str) == 0 &&
187		    str != NULL && strcmp(str, MD_STR_PROCESSOR) == 0) {
188			nproc++;
189			/* check if the physical id exists */
190			if (md_get_prop_val(mdp, list1p[i], MD_STR_ID, &x)
191			    == 0) {
192				procid_flag = 1;
193			}
194		}
195		if (md_get_prop_str(mdp, list1p[i], MD_STR_TYPE, &str) == 0 &&
196		    str && strcmp(str, MD_STR_STRAND) == 0) {
197			ncpu++;
198		}
199	}
200	topo_mod_dprintf(mod, "Found %d procs and %d strands\n", nproc, ncpu);
201	if (nproc == 0 || ncpu == 0) {
202		topo_mod_free(mod, list1p, sizeof (mde_cookie_t) * nnode);
203		return (-1);
204	}
205
206	/* Alloc processors and strand entries */
207	list2p = topo_mod_zalloc(mod, sizeof (mde_cookie_t) * 2 * ncpu);
208	chip->nprocs = nproc;
209	chip->procs = topo_mod_zalloc(mod, nproc * sizeof (md_proc_t));
210	chip->ncpus = ncpu;
211	chip->cpus = topo_mod_zalloc(mod, ncpu * sizeof (md_cpumap_t));
212
213	/* Visit each processor node */
214	procp = chip->procs;
215	mcmp = chip->cpus;
216	for (i = 0, nproc = 0, ncpu = 0; i < ncomp; i++) {
217		if (md_get_prop_str(mdp, list1p[i], MD_STR_TYPE, &str) < 0 ||
218		    str == NULL || strcmp(str, MD_STR_PROCESSOR))
219			continue;
220		if (md_get_prop_val(mdp, list1p[i], MD_STR_SERIAL, &sn) < 0) {
221			if (md_get_prop_str(mdp, list1p[i], MD_STR_SERIAL,
222			    &strserial) < 0) {
223				topo_mod_dprintf(mod,
224				    "Failed to get the serial number of"
225				    "proc[%d]\n", nproc);
226				continue;
227			} else {
228				sn = (uint64_t)strtoull(strserial, &end, 16);
229				if (strserial == end) {
230					topo_mod_dprintf(mod,
231					    "Failed to convert the serial "
232					    "string to serial int of "
233					    "proc[%d]\n", nproc);
234					continue;
235				}
236			}
237		}
238		procp->serialno = sn;
239
240		/* Assign physical proc id */
241		procp->id = -1;
242		if (procid_flag) {
243			if (md_get_prop_val(mdp, list1p[i], MD_STR_ID, &x)
244			    == 0) {
245				procp->id = x;
246			}
247		} else {
248			procp->id = nproc;
249		}
250		topo_mod_dprintf(mod, "proc %d: sn=%llx, id=%d\n", nproc,
251		    procp->serialno, procp->id);
252
253		/* Get all the strands below this proc */
254		cnt = md_scan_dag(mdp,
255		    list1p[i],
256		    md_find_name(mdp, MD_STR_COMPONENT),
257		    md_find_name(mdp, "fwd"),
258		    list2p);
259		topo_mod_dprintf(mod, "proc[%llx]: Found %d fwd components\n",
260		    sn, cnt);
261		if (cnt <= 0) {
262			nproc++;
263			procp++;
264			continue;
265		}
266		for (j = 0; j < cnt; j++) {
267			/* Consider only the strand nodes */
268			if (md_get_prop_str(mdp, list2p[j], MD_STR_TYPE, &str)
269			    < 0 || str == NULL || strcmp(str, MD_STR_STRAND))
270				continue;
271
272			if (md_get_prop_val(mdp, list2p[j], MD_STR_ID, &x) < 0)
273				x = (uint64_t)-1; /* invalid value */
274			mcmp->cpumap_id = x;
275
276			if (md_get_prop_val(mdp, list2p[j], MD_STR_PID, &x) < 0)
277				x = mcmp->cpumap_id;
278			mcmp->cpumap_pid = x;
279
280			mcmp->cpumap_serialno = sn;
281			mcmp->cpumap_chipidx = nproc;
282			ncpu++;
283			mcmp++;
284		}
285
286		/*
287		 * To get the fru of this proc, follow the back arc up to
288		 * find the first node whose fru field is set
289		 */
290		cnt = md_scan_dag(mdp,
291		    list1p[i],
292		    md_find_name(mdp, MD_STR_COMPONENT),
293		    md_find_name(mdp, "back"),
294		    list2p);
295		topo_mod_dprintf(mod, "proc[%d]: Found %d back components\n",
296		    nproc, cnt);
297		if (cnt <= 0) {
298			nproc++;
299			procp++;
300			continue;
301		}
302		for (j = 0; j < cnt; j++) {
303			/* test the fru field which must be positive number */
304			if ((md_get_prop_val(mdp, list2p[j], MD_STR_FRU, &x)
305			    == 0) && x > 0)
306				break;
307		}
308		if (j < cnt) {
309			/* Found the FRU node, get the fru identity */
310			topo_mod_dprintf(mod, "proc[%d] sn=%llx has a fru %d\n",
311			    nproc, procp->serialno, j);
312			frup = topo_mod_zalloc(mod, sizeof (md_fru_t));
313			procp->fru = frup;
314			if (!md_get_prop_str(mdp, list2p[j], MD_STR_NAC, &str))
315				frup->nac = topo_mod_strdup(mod, str);
316			else
317				frup->nac = topo_mod_strdup(mod, MD_FRU_DEF);
318			if (!md_get_prop_str(mdp, list2p[j], MD_STR_PART, &str))
319				frup->part = topo_mod_strdup(mod, str);
320			if (!md_get_prop_str(mdp, list2p[j], MD_STR_SERIAL,
321			    &str))
322				frup->serial = topo_mod_strdup(mod, str);
323			if (!md_get_prop_str(mdp, list2p[j], MD_STR_DASH, &str))
324				frup->dash = topo_mod_strdup(mod, str);
325		} else {
326			topo_mod_dprintf(mod, "proc[%d] sn=%llx has no fru\n",
327			    i, procp->serialno);
328		}
329
330		nproc++;
331		procp++;
332	} /* for i */
333
334	topo_mod_free(mod, list1p, sizeof (mde_cookie_t) * nnode);
335	topo_mod_free(mod, list2p, sizeof (mde_cookie_t) * 2*chip->ncpus);
336
337	return (0);
338}
339
340/*
341 * Extract from the PRI the processor, strand and their fru identity
342 */
343int
344cpu_mdesc_init(topo_mod_t *mod, md_info_t *chip)
345{
346	int rc = -1;
347	md_t *mdp;
348	ssize_t bufsiz = 0;
349	uint64_t *bufp;
350	ldom_hdl_t *lhp;
351	uint32_t type = 0;
352
353	/* get the PRI/MD */
354	if ((lhp = ldom_init(cpu_alloc, cpu_free)) == NULL) {
355		topo_mod_dprintf(mod, "ldom_init() failed\n");
356		return (topo_mod_seterrno(mod, EMOD_NOMEM));
357	}
358
359	(void) ldom_get_type(lhp, &type);
360	if ((type & LDOM_TYPE_CONTROL) != 0) {
361		bufsiz = ldom_get_core_md(lhp, &bufp);
362	} else {
363		bufsiz = ldom_get_local_md(lhp, &bufp);
364	}
365	if (bufsiz <= 0) {
366		topo_mod_dprintf(mod, "failed to get the PRI/MD\n");
367		ldom_fini(lhp);
368		return (-1);
369	}
370
371	if ((mdp = md_init_intern(bufp, cpu_alloc, cpu_free)) == NULL ||
372	    md_node_count(mdp) <= 0) {
373		cpu_free(bufp, (size_t)bufsiz);
374		ldom_fini(lhp);
375		return (-1);
376	}
377
378	/*
379	 * N1 MD contains cpu nodes while N2 MD contains component nodes.
380	 */
381	if (md_find_name(mdp, MD_STR_COMPONENT) != MDE_INVAL_STR_COOKIE) {
382		rc = cpu_n2_mdesc_init(mod, mdp, chip);
383	} else if (md_find_name(mdp, MD_STR_CPU) != MDE_INVAL_STR_COOKIE) {
384		rc =  cpu_n1_mdesc_init(mod, mdp, chip);
385	} else {
386		topo_mod_dprintf(mod, "Unsupported PRI/MD\n");
387		rc = -1;
388	}
389
390	cpu_free(bufp, (size_t)bufsiz);
391	(void) md_fini(mdp);
392	ldom_fini(lhp);
393
394	return (rc);
395}
396
397void
398cpu_mdesc_fini(topo_mod_t *mod, md_info_t *chip)
399{
400	int i;
401	md_proc_t *procp;
402	md_fru_t *frup;
403
404	if (chip->cpus != NULL)
405		topo_mod_free(mod, chip->cpus,
406		    chip->ncpus * sizeof (md_cpumap_t));
407
408	if (chip->procs != NULL) {
409		procp = chip->procs;
410		for (i = 0; i < chip->nprocs; i++) {
411			if ((frup = procp->fru) != NULL) {
412				topo_mod_strfree(mod, frup->nac);
413				topo_mod_strfree(mod, frup->serial);
414				topo_mod_strfree(mod, frup->part);
415				topo_mod_strfree(mod, frup->dash);
416				topo_mod_free(mod, frup, sizeof (md_fru_t));
417			}
418			procp++;
419		}
420		topo_mod_free(mod, chip->procs,
421		    chip->nprocs * sizeof (md_proc_t));
422	}
423}
424