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