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