xref: /illumos-gate/usr/src/cmd/ldmad/ldma_dio.c (revision fc256490)
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 
46 static int get_devinfo(uint8_t **mdpp, size_t *size);
47 static boolean_t is_root_complex(di_prom_handle_t ph, di_node_t di);
48 static 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);
50 static int create_children(mmd_t *mdp,
51     di_prom_handle_t ph, md_node_t *node, di_node_t parent);
52 static int create_peers(mmd_t *mdp,
53     di_prom_handle_t ph, md_node_t *node, di_node_t dev);
54 static 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) */
62 static 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 
68 static ldm_msg_func_t ldma_dio_pcidev_info_handler;
69 
70 static ldma_msg_handler_t ldma_dio_handlers[] = {
71 	{MSGDIO_PCIDEV_INFO, ldma_dio_pcidev_info_handler},
72 };
73 
74 ldma_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 */
81 static ldma_request_status_t
82 ldma_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 
114 static boolean_t
115 is_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  */
145 static int
146 add_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  */
176 static int
177 add_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 
202 static md_node_t *
203 link_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 
237 fail:
238 	md_free_node(mdp, np);
239 	return (NULL);
240 }
241 
242 static int
243 create_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 
283 static int
284 create_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 
298 static int
299 device_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 
335 static int
336 get_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