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 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <string.h>
28#include <strings.h>
29#include <umem.h>
30#include <sys/mdesc.h>
31#include <sys/systeminfo.h>
32#include <sys/fm/ldom.h>
33
34#include <hb_mdesc.h>
35
36#include "hb_rcid.h"
37
38static void *
39hb_alloc(size_t size)
40{
41	return (umem_alloc(size, UMEM_DEFAULT));
42}
43
44static void
45hb_free(void *data, size_t size)
46{
47	umem_free(data, size);
48}
49
50/*
51 * hb_find_hb()
52 * Description:
53 *     Return the pointer of hostbridge entry
54 */
55md_hb_t *
56hb_find_hb(md_info_t *phbmd, int hbid) {
57	int i;
58	md_hb_t *phb;
59
60	/* search the processor based on the physical id */
61	for (i = 0, phb = phbmd->hbs; i < phbmd->nhbs; i++, phb++) {
62		if (phb->rcs != NULL && phb->id == hbid) {
63			return (phb);
64		}
65	}
66
67	return (NULL);
68}
69
70/*
71 * hb_rc_init()
72 * Description:
73 *     Read the hostbridge/pciexrc information from the MD
74 *     The hostbridge/pciexrc information is not specified in the PRI of
75 *     the existing sun4v platforms, the enumerator assumes there is only
76 *     one hostbridge and its physical id is 0. It will create all the
77 *     pciexrc nodes under the topo node hostbridge=0.
78 */
79static int
80hb_rc_init(topo_mod_t *mod, md_t *mdp, md_info_t *hbmdp)
81{
82	int i, rc;
83	int id, nnode, nio, nrcs;
84	char *s = NULL;
85	uint64_t x;
86	mde_cookie_t *listp;
87	md_hb_t *hbp;
88	char platform[MAXNAMELEN];
89
90	bzero(hbmdp, sizeof (md_info_t));
91	nnode = md_node_count(mdp);
92	listp = topo_mod_zalloc(mod, sizeof (mde_cookie_t) * nnode);
93
94	/* find the pciex bus nodes */
95	nio = md_scan_dag(mdp,
96	    MDE_INVAL_ELEM_COOKIE,
97	    md_find_name(mdp, MD_STR_IODEVICE),
98	    md_find_name(mdp, "fwd"),
99	    listp);
100	if (nio <= 0) {
101		topo_mod_dprintf(mod, "iodevice nodes not found\n");
102		topo_mod_free(mod, listp, sizeof (mde_cookie_t) * nnode);
103		return (-1);
104	}
105	topo_mod_dprintf(mod, "Found %d %s nodes\n", nio, MD_STR_IODEVICE);
106
107	for (i = 0, nrcs = 0; i < nio; i++) {
108		rc = md_get_prop_str(mdp, listp[i], MD_STR_DEVICE_TYPE, &s);
109		if ((rc == 0) && (s != NULL) && (strcmp(s, MD_STR_PCIEX) == 0))
110			nrcs++;
111	}
112	topo_mod_dprintf(mod, "Found %d pciex buses\n", nrcs);
113	if (nrcs == 0) {
114		topo_mod_dprintf(mod, "pciex nodes not found\n");
115		topo_mod_free(mod, listp, sizeof (mde_cookie_t) * nnode);
116		return (-1);
117	}
118
119	platform[0] = '\0';
120	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
121
122	/*
123	 * All existing sun4v platforms have only one hostdridge.
124	 */
125	hbmdp->shbs = hbmdp->nhbs = 1;
126	hbp = topo_mod_zalloc(mod, sizeof (md_hb_t) * hbmdp->nhbs);
127	hbp->id = 0;
128	hbmdp->hbs = hbp;
129
130	hbp->srcs = nrcs;
131	hbp->rcs = topo_mod_zalloc(mod, sizeof (md_rc_t) * nrcs);
132	hbp->nrcs = 0;
133	for (i = 0, nrcs = 0; i < nio; i++) {
134		rc = md_get_prop_str(mdp, listp[i], MD_STR_DEVICE_TYPE, &s);
135		if ((rc != 0) || s == NULL || strcmp(s, MD_STR_PCIEX) != 0)
136			continue;
137
138		hbp->rcs[nrcs].id = -1;		/* invalidate the entry */
139
140		/* bus address */
141		if (md_get_prop_val(mdp, listp[i], MD_STR_CFGHDL, &x) < 0) {
142			nrcs++;
143			continue;
144		}
145		hbp->rcs[nrcs].cfg_handle = x;
146		topo_mod_dprintf(mod, "Found rc=%d ba=%llx\n", nrcs, x);
147
148		/* Assign the physical id of the pciexrc */
149		if ((id = hb_find_rc_pid(platform, x)) >= 0)
150			hbp->rcs[nrcs].id = id;
151		else
152			hbp->rcs[nrcs].id = hbp->nrcs;
153
154		nrcs++;
155		hbp->nrcs++;
156	}
157
158	topo_mod_free(mod, listp, sizeof (mde_cookie_t) * nnode);
159
160	return (0);
161}
162
163/*
164 * Get the info. of the hb and rc from the PRI/MD
165 */
166int
167hb_mdesc_init(topo_mod_t *mod, md_info_t *phbmd)
168{
169	int rc = -1;
170	md_t *mdp;
171	ssize_t bufsiz = 0;
172	uint64_t *bufp;
173	uint32_t type = 0;
174	ldom_hdl_t *lhp;
175
176	/* get the PRI/MD */
177	if ((lhp = ldom_init(hb_alloc, hb_free)) == NULL) {
178		topo_mod_dprintf(mod, "ldom_init() failed\n");
179		return (topo_mod_seterrno(mod, EMOD_NOMEM));
180	}
181
182	(void) ldom_get_type(lhp, &type);
183	if ((type & LDOM_TYPE_CONTROL) != 0) {
184		bufsiz = ldom_get_core_md(lhp, &bufp);
185	} else {
186		bufsiz = ldom_get_local_md(lhp, &bufp);
187	}
188	if (bufsiz <= 0) {
189		topo_mod_dprintf(mod, "failed to get the PRI/MD\n");
190		ldom_fini(lhp);
191		return (-1);
192	}
193
194	if ((mdp = md_init_intern(bufp, hb_alloc, hb_free)) == NULL ||
195	    md_node_count(mdp) <= 0) {
196		hb_free(bufp, (size_t)bufsiz);
197		ldom_fini(lhp);
198		return (-1);
199	}
200
201	rc = hb_rc_init(mod, mdp, phbmd);
202
203	hb_free(bufp, (size_t)bufsiz);
204	(void) md_fini(mdp);
205	ldom_fini(lhp);
206
207	return (rc);
208}
209
210void
211hb_mdesc_fini(topo_mod_t *mod, md_info_t *hbmdp)
212{
213	int i;
214	md_hb_t *hbp;
215
216	if (hbmdp->hbs == NULL)
217		return;
218
219	for (i = 0, hbp = hbmdp->hbs; i < hbmdp->nhbs; i++, hbp++) {
220		if (hbp->rcs == NULL)
221			continue;
222		topo_mod_free(mod, hbp->rcs, hbp->srcs * sizeof (md_rc_t));
223	}
224	topo_mod_free(mod, hbmdp->hbs, hbmdp->shbs * sizeof (md_hb_t));
225
226}
227