2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Create a topology node for a PRI node of type 'pciexrc'
2N/A */
2N/A#include <sys/types.h>
2N/A#include <strings.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <fm/topo_hc.h>
2N/A#include <libdevinfo.h>
2N/A#include <sys/pci.h>
2N/A#include "pi_impl.h"
2N/A
2N/A#define PCIEX_MAX_DEVICE 255
2N/A#define PCIEX_MAX_BDF_SIZE 23 /* '0x' + sizeof (UNIT64_MAX) + '\0' */
2N/A
2N/A#define TOPO_PGROUP_PCIEX "pciex"
2N/A#define _ENUM_NAME "enum_pciexrc"
2N/A
2N/Astatic char *drv_name = NULL;
2N/A
2N/Astatic const topo_pgroup_info_t io_pgroup =
2N/A { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/A
2N/Astatic const topo_pgroup_info_t pci_pgroup =
2N/A { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/A
2N/Astatic int pi_enum_pciexrc_finddev(topo_mod_t *, md_t *, mde_cookie_t,
2N/A tnode_t *);
2N/A
2N/Astatic char *pi_enum_pciexrc_findbdf(topo_mod_t *, di_node_t);
2N/A
2N/Astatic int pi_enum_pciexrc_defer(topo_mod_t *, md_t *, mde_cookie_t,
2N/A topo_instance_t, tnode_t *, const char *, tnode_t *, void *);
2N/A
2N/A
2N/A/*
2N/A * Create a pciexrc topo by calling the pciexrc enumerator for this instance.
2N/A */
2N/Aint
2N/Api_enum_pciexrc(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
2N/A topo_instance_t inst, tnode_t *t_parent, const char *hc_name,
2N/A tnode_t **t_node)
2N/A{
2N/A int result;
2N/A
2N/A topo_mod_dprintf(mod, "%s called for node_0x%llx type %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node, hc_name);
2N/A
2N/A *t_node = NULL;
2N/A
2N/A /*
2N/A * Create the root complex topo node. Use the generic enumerator to
2N/A * do this, and then we will add more attributes below.
2N/A */
2N/A result = pi_enum_generic_impl(mod, mdp, mde_node, inst, t_parent,
2N/A t_parent, hc_name, _ENUM_NAME, t_node, 0);
2N/A if (result != 0 || *t_node == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to create topo node: %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node,
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A return (result);
2N/A }
2N/A
2N/A /* Update the topo node with more specific information */
2N/A result = pi_enum_update(mod, mdp, mde_node, t_parent, *t_node,
2N/A hc_name);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to create node properites: %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node,
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A return (result);
2N/A }
2N/A
2N/A result = pi_enum_pciexrc_finddev(mod, mdp, mde_node, *t_node);
2N/A if (result == 0) {
2N/A /*
2N/A * The node exists in this domain. We will call the PCIBUS
2N/A * enumerator after the entire PRI graph has been walked so
2N/A * that all the possible FRU nodes are available for bus's
2N/A * that span multiple FRU boundaries.
2N/A */
2N/A result = pi_defer_add(mod, mde_node, t_parent, *t_node,
2N/A pi_enum_pciexrc_defer, NULL);
2N/A if (result != 0) {
2N/A /* We cannot defer the call, so we need to do it now */
2N/A result = pi_enum_pciexrc_defer(mod, mdp, mde_node, inst,
2N/A t_parent, hc_name, *t_node, NULL);
2N/A }
2N/A } else {
2N/A /*
2N/A * It is OK if the node does not exist for further PCIBUS
2N/A * enumeration. We can return success having created the
2N/A * root complex node itself.
2N/A */
2N/A result = 0;
2N/A }
2N/A topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node, hc_name);
2N/A
2N/A return (result);
2N/A}
2N/A
2N/A
2N/A/* ARGSUSED */
2N/Astatic int
2N/Api_enum_pciexrc_defer(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
2N/A topo_instance_t inst, tnode_t *t_parent, const char *hc_name,
2N/A tnode_t *t_node, void *private)
2N/A{
2N/A int result;
2N/A topo_instance_t min;
2N/A topo_instance_t max;
2N/A
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx deferred enumeration starting\n", _ENUM_NAME,
2N/A (uint64_t)mde_node);
2N/A
2N/A /* Make sure our dependent modules are loaded */
2N/A if (topo_mod_load(mod, PCI_BUS, TOPO_VERSION) == NULL) {
2N/A topo_mod_dprintf(mod, "%s could not load %s module: %s\n",
2N/A _ENUM_NAME, PCI_BUS, topo_strerror(topo_mod_errno(mod)));
2N/A return (-1);
2N/A }
2N/A
2N/A /* Create a node range for children of this bus */
2N/A min = 0;
2N/A max = PCIEX_MAX_DEVICE;
2N/A result = topo_node_range_create(mod, t_node, PCI_BUS, min, max);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to create node range: %s\n",
2N/A _ENUM_NAME, topo_strerror(topo_mod_errno(mod)));
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Invoke the pcibus enumerator for this node.
2N/A */
2N/A result = topo_mod_enumerate(mod, t_node, PCI_BUS, PCIEX_BUS,
2N/A min, max, NULL);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx enumeration failed: %s\n", _ENUM_NAME,
2N/A (uint64_t)mde_node, topo_strerror(topo_mod_errno(mod)));
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node, hc_name);
2N/A
2N/A return (result);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Update PCIEXRC/HOSTBRIDGE topo node with node-specific information
2N/A *
2N/A * The following is mostly a duplicate of code contained in:
2N/A * usr/src/lib/fm/topo/modules/sun4v/cpuboard/
2N/A * cpuboard_hostbridge.c:cpuboard_rc_node_create
2N/A */
2N/Aint
2N/Api_enum_update(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
2N/A tnode_t *t_parent, tnode_t *t_node, const char *hc_name)
2N/A{
2N/A int result;
2N/A int err;
2N/A int is_hbridge = 0;
2N/A int is_pciexrc = 0;
2N/A char *path = NULL;
2N/A char *bdf = NULL;
2N/A char *_enum_name;
2N/A nvlist_t *modfmri;
2N/A nvlist_t *devfmri;
2N/A di_node_t devtree;
2N/A di_node_t dnode;
2N/A
2N/A /*
2N/A * Determine if decorating a PCIE root complex or a hostbridge
2N/A * node.
2N/A */
2N/A if (strncmp(hc_name, PCIEX_ROOT, strlen(hc_name)) == 0) {
2N/A is_pciexrc = 1;
2N/A _enum_name = "enum_pciexrc";
2N/A } else if (strncmp(hc_name, HOSTBRIDGE, strlen(hc_name)) == 0) {
2N/A is_hbridge = 1;
2N/A _enum_name = "enum_hostbridge";
2N/A } else {
2N/A topo_mod_dprintf(mod,
2N/A "pi_enum_update node_0x%llx unknown hc name %s\n",
2N/A (uint64_t)mde_node, hc_name);
2N/A return (-1);
2N/A }
2N/A
2N/A if (t_parent == NULL || t_node == NULL) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx has no parent\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Calculate the device path for this root complex node.
2N/A */
2N/A path = pi_get_path(mod, mdp, mde_node);
2N/A if (path == NULL) {
2N/A if (is_hbridge == 1) {
2N/A /* "path" not required for hostbridge */
2N/A return (0);
2N/A }
2N/A topo_mod_dprintf(mod, "%s node_0x%llx has no path\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Set the ASRU for this node using the dev scheme
2N/A */
2N/A devfmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, path, NULL);
2N/A if (devfmri == NULL) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx fmri creation failed\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A result = -1;
2N/A goto out;
2N/A }
2N/A
2N/A result = topo_node_asru_set(t_node, devfmri, 0, &err);
2N/A nvlist_free(devfmri);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx failed to set ASRU\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Create property groups.
2N/A */
2N/A result = topo_pgroup_create(t_node, &io_pgroup, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx "
2N/A "topo_pgroup_create for io pgroup failed\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A if (is_pciexrc == 1) {
2N/A result = topo_pgroup_create(t_node, &pci_pgroup, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx "
2N/A "topo_pgroup_create for pci pgroup failed\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEV,
2N/A TOPO_PROP_IMMUTABLE, path, &err);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set DEV property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A /* device type is always "pciex" */
2N/A result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
2N/A TOPO_PROP_IMMUTABLE, TOPO_PGROUP_PCIEX, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set DEVTYPE property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Derived the driver name from the device path.
2N/A */
2N/A topo_mod_dprintf(mod, "%s node_0x%llx find di node for path(%s)\n",
2N/A _enum_name, (uint64_t)mde_node, path);
2N/A devtree = topo_mod_devinfo(mod);
2N/A dnode = di_lookup_node(devtree, path);
2N/A if (dnode == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx failed to get node\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A result = -1;
2N/A goto out;
2N/A }
2N/A drv_name = di_driver_name(dnode);
2N/A if (drv_name == NULL) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx failed to get driver "
2N/A " name\n", _enum_name, (uint64_t)mde_node);
2N/A result = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (is_pciexrc == 1) {
2N/A /*
2N/A * Derived the BDF property from the devinfo node.
2N/A */
2N/A bdf = pi_enum_pciexrc_findbdf(mod, dnode);
2N/A if (bdf == NULL) {
2N/A topo_mod_dprintf(mod, "%s: node_0x%llx failed to "
2N/A "find BDF", _enum_name, (uint64_t)mde_node);
2N/A result = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s node_0x%llx driver name is %s\n",
2N/A _enum_name, (uint64_t)mde_node, drv_name);
2N/A
2N/A result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
2N/A TOPO_PROP_IMMUTABLE, drv_name, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set DRIVER property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A modfmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, drv_name);
2N/A if (modfmri == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to create module fmri\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A result = -1;
2N/A goto out;
2N/A }
2N/A result = topo_prop_set_fmri(t_node, TOPO_PGROUP_IO, TOPO_IO_MODULE,
2N/A TOPO_PROP_IMMUTABLE, modfmri, &err);
2N/A nvlist_free(modfmri);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set MODULE property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A if (is_pciexrc == 1) {
2N/A /* This is a PCIEX root complex */
2N/A result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set EXCAP property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A /* Set BDF for root complex */
2N/A result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, bdf, &err);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to set BDF property\n",
2N/A _enum_name, (uint64_t)mde_node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A goto out;
2N/A }
2N/A
2N/A /* Create a node range for the children of this root complex */
2N/A result = topo_node_range_create(mod, t_node, PCIEX_BUS, 0,
2N/A PCIEX_MAX_DEVICE);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s node_0x%llx failed to create %s range\n",
2N/A _enum_name, (uint64_t)mde_node, PCIEX_BUS);
2N/A result = -1;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (path != NULL) {
2N/A topo_mod_strfree(mod, path);
2N/A }
2N/A if (bdf != NULL) {
2N/A topo_mod_strfree(mod, bdf);
2N/A }
2N/A return (result);
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Api_enum_pciexrc_finddev(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
2N/A tnode_t *t_node)
2N/A{
2N/A di_node_t devtree;
2N/A di_node_t dnode;
2N/A char *path;
2N/A
2N/A /* Initialize the device information structure for this module */
2N/A devtree = topo_mod_devinfo(mod);
2N/A if (devtree == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "devinfo init failed\n");
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Find the PRI node path property. This will be used to associate
2N/A * the PRI node with the device node.
2N/A */
2N/A path = pi_get_path(mod, mdp, mde_node);
2N/A if (path == NULL) {
2N/A topo_mod_dprintf(mod, "node_0x%llx has no path\n",
2N/A (uint64_t)mde_node);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Scan the device node list and find the node associated with
2N/A * the given PRI node. Equality is defined when the PRI path
2N/A * is the same as the device node path.
2N/A */
2N/A dnode = di_drv_first_node(drv_name, devtree);
2N/A while (dnode != DI_NODE_NIL) {
2N/A char *devfs_path;
2N/A
2N/A devfs_path = di_devfs_path(dnode);
2N/A if (devfs_path != NULL) {
2N/A if (strncmp(devfs_path, path, strlen(path)) == 0) {
2N/A /* We have found the matching dnode */
2N/A di_devfs_path_free(devfs_path);
2N/A break;
2N/A }
2N/A di_devfs_path_free(devfs_path);
2N/A }
2N/A
2N/A /* We have not found the matching dnode yet */
2N/A dnode = di_drv_next_node(dnode);
2N/A }
2N/A if (dnode != DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "%s node_0x%llx found dev path %s\n",
2N/A _ENUM_NAME, (uint64_t)mde_node, path);
2N/A
2N/A /*
2N/A * Associate this dnode with the topo node. The PCI
2N/A * enumerator requires this information.
2N/A */
2N/A topo_node_setspecific(t_node, (void *)dnode);
2N/A }
2N/A
2N/A topo_mod_strfree(mod, path);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Find the BDF property and return as a string.
2N/A *
2N/A * The string must be freed with topo_mod_strfree()
2N/A */
2N/Astatic char *
2N/Api_enum_pciexrc_findbdf(topo_mod_t *mod, di_node_t dnode)
2N/A{
2N/A uint_t reg;
2N/A uint_t bdf;
2N/A char bdf_str[PCIEX_MAX_BDF_SIZE];
2N/A unsigned char *buf;
2N/A di_prop_t di_prop;
2N/A di_prom_handle_t di_prom_hdl;
2N/A di_prom_prop_t di_prom_prop;
2N/A
2N/A /*
2N/A * Look for the "reg" property from the devinfo node.
2N/A */
2N/A for (di_prop = di_prop_next(dnode, DI_PROP_NIL);
2N/A di_prop != DI_PROP_NIL;
2N/A di_prop = di_prop_next(dnode, di_prop)) {
2N/A if (strncmp(di_prop_name(di_prop), "reg",
2N/A sizeof (reg)) == 0) {
2N/A if (di_prop_bytes(di_prop, &buf) < sizeof (uint_t)) {
2N/A continue;
2N/A }
2N/A bcopy(buf, &reg, sizeof (uint_t));
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If the "reg" property is not found in the di_node; look for it in
2N/A * OBP prom data.
2N/A */
2N/A if (di_prop == DI_PROP_NIL) {
2N/A if ((di_prom_hdl = topo_mod_prominfo(mod)) ==
2N/A DI_PROM_HANDLE_NIL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s failed to get prom handle\n", _ENUM_NAME);
2N/A return (NULL);
2N/A }
2N/A for (di_prom_prop =
2N/A di_prom_prop_next(di_prom_hdl, dnode, DI_PROM_PROP_NIL);
2N/A di_prom_prop != DI_PROM_PROP_NIL;
2N/A di_prom_prop =
2N/A di_prom_prop_next(di_prom_hdl, dnode, di_prom_prop)) {
2N/A if (strncmp(di_prom_prop_name(di_prom_prop), "reg",
2N/A sizeof (reg)) == 0) {
2N/A if (di_prom_prop_data(di_prom_prop, &buf) <
2N/A sizeof (uint_t)) {
2N/A continue;
2N/A }
2N/A bcopy(buf, &reg, sizeof (uint_t));
2N/A break;
2N/A }
2N/A }
2N/A if (di_prom_prop == DI_PROP_NIL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s failed to get reg property\n", _ENUM_NAME);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Caculate BDF
2N/A *
2N/A * The reg property is divided like this:
2N/A * -----------------------------------------------------
2N/A * | 23 Bus 16 | 15 Dev 11 | 10 Fn 8 | 7 Reg 0 |
2N/A * -----------------------------------------------------
2N/A *
2N/A * PCI_REG_* macros strip off Reg and shift to get individual
2N/A * Bus/Dev/Fn bits. Shift and OR each to get bdf value.
2N/A */
2N/A bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) |
2N/A PCI_REG_FUNC_G(reg);
2N/A
2N/A /* Pass BDF back as a string */
2N/A (void) snprintf(bdf_str, PCIEX_MAX_BDF_SIZE, "0x%x", bdf);
2N/A topo_mod_dprintf(mod, "%s found BDF %s\n", _ENUM_NAME, bdf_str);
2N/A
2N/A return (topo_mod_strdup(mod, bdf_str));
2N/A}