ldma_dio.c revision 11596:e9010337bcd3
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 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <alloca.h>
33#include <sys/stat.h>
34#include <malloc.h>
35#include <fcntl.h>
36#include <syslog.h>
37#include <string.h>
38#include <errno.h>
39#include <sys/mdesc.h>
40#include <sys/mdesc_impl.h>
41#include <libdevinfo.h>
42#include "ldma.h"
43#include "mdesc_mutable.h"
44
45
46static int get_devinfo(uint8_t **mdpp, size_t *size);
47static boolean_t is_root_complex(di_prom_handle_t ph, di_node_t di);
48static md_node_t *link_device_node(mmd_t *mdp,
49    di_prom_handle_t ph, di_node_t di, md_node_t *node, char *path);
50static int create_children(mmd_t *mdp,
51    di_prom_handle_t ph, md_node_t *node, di_node_t parent);
52static int create_peers(mmd_t *mdp,
53    di_prom_handle_t ph, md_node_t *node, di_node_t dev);
54static int device_tree_to_md(mmd_t *mdp, md_node_t *top);
55
56
57#define	PCIEX		"pciex"
58#define	LDMA_MODULE	LDMA_NAME_DIO
59
60
61/* System Info version supported (only version 1.0) */
62static ds_ver_t ldma_dio_vers[] = { {1, 0} };
63
64#define	LDMA_DIO_NVERS	(sizeof (ldma_dio_vers) / sizeof (ds_ver_t))
65#define	LDMA_DIO_NHANDLERS  (sizeof (ldma_dio_handlers) /		\
66    sizeof (ldma_msg_handler_t))
67
68static ldm_msg_func_t ldma_dio_pcidev_info_handler;
69
70static ldma_msg_handler_t ldma_dio_handlers[] = {
71	{MSGDIO_PCIDEV_INFO, ldma_dio_pcidev_info_handler},
72};
73
74ldma_agent_info_t ldma_dio_info = {
75	LDMA_NAME_DIO,
76	ldma_dio_vers, LDMA_DIO_NVERS,
77	ldma_dio_handlers, LDMA_DIO_NHANDLERS
78};
79
80/* ARGSUSED */
81static ldma_request_status_t
82ldma_dio_pcidev_info_handler(ds_ver_t *ver, ldma_message_header_t *request,
83    size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
84{
85	ldma_message_header_t *reply;
86	char *data;
87	uint8_t *md_bufp = NULL;
88	size_t md_size;
89	int rv;
90
91	LDMA_DBG("%s: PCI device info request", __func__);
92	rv  = get_devinfo(&md_bufp, &md_size);
93	if (rv != 0) {
94		LDMA_ERR("Failed to generate devinfo MD");
95		return (LDMA_REQ_FAILED);
96	}
97	reply = ldma_alloc_result_msg(request, md_size);
98	if (reply == NULL) {
99		LDMA_ERR("Memory allocation failure");
100		free(md_bufp);
101		return (LDMA_REQ_FAILED);
102	}
103
104	reply->msg_info = md_size;
105	data = LDMA_HDR2DATA(reply);
106	(void) memcpy(data, md_bufp, md_size);
107	*replyp = reply;
108	*reply_dlenp = md_size;
109	free(md_bufp);
110	LDMA_DBG("%s: sending PCI device info", __func__);
111	return (LDMA_REQ_COMPLETED);
112}
113
114static boolean_t
115is_root_complex(di_prom_handle_t ph, di_node_t di)
116{
117	int	len;
118	char	*type;
119
120	len = di_prom_prop_lookup_strings(ph, di, "device_type", &type);
121	if ((len == 0) || (type == NULL))
122		return (B_FALSE);
123
124	if (strcmp(type, PCIEX) != 0)
125		return (B_FALSE);
126
127	/*
128	 * A root complex node is directly under the root node.  So, if
129	 * 'di' is not the root node, and its parent has no parent,
130	 * then 'di' represents a root complex node.
131	 */
132	return ((di_parent_node(di) != DI_NODE_NIL) &&
133	    (di_parent_node(di_parent_node(di)) == DI_NODE_NIL));
134}
135
136/*
137 * String properties in the prom can contain multiple null-terminated
138 * strings which are concatenated together.  We must represent them in
139 * an MD as a data property.  This function retrieves such a property
140 * and adds it to the MD.  If the 'alt_name' PROM property exists then
141 * the MD property is created with the value of the PROM 'alt_name'
142 * property, otherwise it is created with the value of the PROM 'name'
143 * property.
144 */
145static int
146add_prom_string_prop(di_prom_handle_t ph,
147    mmd_t *mdp, md_node_t *np, di_node_t di, char *name, char *alt_name)
148{
149	int		count;
150	char		*pp_data = NULL;
151	char		*str;
152	int		rv = 0;
153
154	if (alt_name != NULL) {
155		count = di_prom_prop_lookup_strings(ph, di, alt_name, &pp_data);
156	}
157	if (pp_data == NULL) {
158		count = di_prom_prop_lookup_strings(ph, di, name, &pp_data);
159	}
160
161	if (count > 0 && pp_data != NULL) {
162		for (str = pp_data; count > 0; str += strlen(str) + 1)
163			count--;
164		rv = md_add_data_property(mdp,
165		    np, name, str - pp_data, (uint8_t *)pp_data);
166	}
167	return (rv);
168}
169
170/*
171 * Add an int property 'name' to an MD from an existing PROM property. If
172 * the 'alt_name' PROM property exists then the MD property is created with
173 * the value of the PROM 'alt_name' property, otherwise it is created with
174 * the value of the PROM 'name' property.
175 */
176static int
177add_prom_int_prop(di_prom_handle_t ph,
178    mmd_t *mdp, md_node_t *np, di_node_t di, char *name, char *alt_name)
179{
180	int		count;
181	int		rv = 0;
182	int		*pp_data = NULL;
183
184	if (alt_name != NULL) {
185		count = di_prom_prop_lookup_ints(ph, di, alt_name, &pp_data);
186	}
187	if (pp_data == NULL) {
188		count = di_prom_prop_lookup_ints(ph, di, name, &pp_data);
189	}
190
191	/*
192	 * Note: We know that the properties of interest contain a
193	 * a single int.
194	 */
195	if (count > 0 && pp_data != NULL) {
196		ASSERT(count == 1);
197		rv = md_add_value_property(mdp, np, name, *pp_data);
198	}
199	return (rv);
200}
201
202static md_node_t *
203link_device_node(mmd_t *mdp,
204    di_prom_handle_t ph, di_node_t di, md_node_t *node, char *path)
205{
206	md_node_t	*np;
207
208	np = md_link_new_node(mdp, "iodevice", node, "fwd", "back");
209	if (np == NULL)
210		return (NULL);
211
212	/* Add the properties from the devinfo node. */
213	if (md_add_string_property(mdp, np, "dev_path", path) != 0)
214		goto fail;
215
216	/* Add the required properties for this node. */
217	if (add_prom_string_prop(ph, mdp, np, di, "device_type", NULL) != 0)
218		goto fail;
219
220	if (add_prom_string_prop(ph, mdp, np, di, "compatible", NULL) != 0)
221		goto fail;
222
223	if (add_prom_int_prop(ph,
224	    mdp, np, di, "device-id", "real-device-id") != 0)
225		goto fail;
226
227	if (add_prom_int_prop(ph,
228	    mdp, np, di, "vendor-id", "real-vendor-id") != 0)
229		goto fail;
230
231	if (add_prom_int_prop(ph,
232	    mdp, np, di, "class-code", "real-class-code") != 0)
233		goto fail;
234
235	return (np);
236
237fail:
238	md_free_node(mdp, np);
239	return (NULL);
240}
241
242static int
243create_children(mmd_t *mdp,
244    di_prom_handle_t ph, md_node_t *md_parent, di_node_t di_parent)
245{
246	md_node_t	*md_node;
247	md_node_t	*md_child;
248	di_node_t	di_child;
249	char		*path;
250	int		rv;
251
252	path = di_devfs_path(di_parent);
253	if (path == NULL)
254		return (EIO);
255
256	md_node = link_device_node(mdp, ph, di_parent, md_parent, path);
257	di_devfs_path_free(path);
258	if (md_node == NULL) {
259		return (ENOMEM);
260	}
261
262	while ((di_child = di_child_node(di_parent)) != DI_NODE_NIL) {
263		path = di_devfs_path(di_child);
264		if (path != NULL) {
265			md_child = link_device_node(mdp,
266			    ph, di_child, md_node, path);
267			di_devfs_path_free(path);
268			if (md_child == NULL) {
269				return (ENOMEM);
270			}
271		}
272
273		rv = create_peers(mdp, ph, md_node, di_child);
274		if (rv != 0)
275			return (rv);
276
277		md_node = md_child;
278		di_parent = di_child;
279	}
280	return (0);
281}
282
283static int
284create_peers(mmd_t *mdp, di_prom_handle_t ph, md_node_t *node, di_node_t dev)
285{
286	di_node_t	di_peer;
287	int		rv;
288
289	while ((di_peer = di_sibling_node(dev)) != DI_NODE_NIL) {
290		rv = create_children(mdp, ph, node, di_peer);
291		if (rv != 0)
292			return (rv);
293		dev = di_peer;
294	}
295	return (0);
296}
297
298static int
299device_tree_to_md(mmd_t *mdp, md_node_t *top)
300{
301	di_node_t		node;
302	di_node_t		root;
303	di_prom_handle_t	ph;
304	int			rv = 0;
305
306	root = di_init("/", DINFOSUBTREE | DINFOPROP);
307
308	if (root == DI_NODE_NIL) {
309		LDMA_ERR("di_init cannot find device tree root node.");
310		return (errno);
311	}
312
313	ph = di_prom_init();
314	if (ph == DI_PROM_HANDLE_NIL) {
315		LDMA_ERR("di_prom_init failed.");
316		di_fini(root);
317		return (errno);
318	}
319
320	node = di_child_node(root);
321	while (node != NULL) {
322		if (is_root_complex(ph, node)) {
323			rv = create_children(mdp, ph, top, node);
324			if (rv != 0)
325				break;
326		}
327		node = di_sibling_node(node);
328	}
329
330	di_prom_fini(ph);
331	di_fini(root);
332	return (rv);
333}
334
335static int
336get_devinfo(uint8_t **mdpp, size_t *size)
337{
338	mmd_t		*mdp;
339	md_node_t	*rootp;
340	size_t		md_size;
341	uint8_t		*md_bufp;
342
343	mdp = md_new_md();
344	if (mdp == NULL) {
345		return (ENOMEM);
346	}
347	rootp = md_new_node(mdp, "root");
348	if (rootp == NULL) {
349		md_destroy(mdp);
350		return (ENOMEM);
351	}
352
353	if (device_tree_to_md(mdp, rootp) != 0) {
354		md_destroy(mdp);
355		return (ENOMEM);
356	}
357	md_size = (int)md_gen_bin(mdp, &md_bufp);
358
359	if (md_size == 0) {
360		md_destroy(mdp);
361		return (EIO);
362	}
363	*mdpp = md_bufp;
364	*size = md_size;
365
366	md_destroy(mdp);
367	return (0);
368}
369