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) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <alloca.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <limits.h>
2N/A#include <sys/types.h>
2N/A#include <sys/pci.h>
2N/A#include <sys/pcie.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 <hostbridge.h>
2N/A#include <pcibus.h>
2N/A#include <did.h>
2N/A#include <did_props.h>
2N/A#include <fm/libtopo.h>
2N/A
2N/Astatic int ASRU_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int FRU_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int DEVprop_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int DRIVERprop_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int MODULEprop_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int EXCAP_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int BDF_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int label_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int maybe_di_chars_copy(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int maybe_di_uint_to_str(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int maybe_di_uint_to_dec_str(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/Astatic int AADDR_set(tnode_t *, did_t *,
2N/A const char *, const char *, const char *);
2N/A
2N/A/*
2N/A * Arrays of "property translation routines" to set the properties a
2N/A * given type of topology node should have.
2N/A *
2N/A * Note that the label_set translation *MUST COME BEFORE* the FRU
2N/A * translation. For the near term we're setting the FRU fmri to
2N/A * be a legacy-hc style FMRI based on the label, so the label needs
2N/A * to have been set before we do the FRU translation.
2N/A *
2N/A */
2N/A
2N/Astatic const topo_pgroup_info_t io_pgroup =
2N/A { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
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 const topo_pgroup_info_t protocol_pgroup = {
2N/A TOPO_PGROUP_PROTOCOL,
2N/A TOPO_STABILITY_PRIVATE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A 1
2N/A}; /* Request to create protocol will be ignored by libtopo */
2N/A
2N/Atxprop_t Fn_common_props[] = {
2N/A { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
2N/A { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
2N/A { DI_DEVIDPROP, &pci_pgroup, TOPO_PCI_DEVID, maybe_di_uint_to_str },
2N/A { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
2N/A { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
2N/A { "serd_io_device_nonfatal_n", &io_pgroup, "serd_io_device_nonfatal_n",
2N/A maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_t", &io_pgroup, "serd_io_device_nonfatal_t",
2N/A maybe_di_chars_copy },
2N/A { "serd_io_device_nonfatal_btlp_n", &io_pgroup,
2N/A "serd_io_device_nonfatal_btlp_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_btlp_t", &io_pgroup,
2N/A "serd_io_device_nonfatal_btlp_t", maybe_di_chars_copy },
2N/A { "serd_io_device_nonfatal_bdllp_n", &io_pgroup,
2N/A "serd_io_device_nonfatal_bdllp_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_bdllp_t", &io_pgroup,
2N/A "serd_io_device_nonfatal_bdllp_t", maybe_di_chars_copy },
2N/A { "serd_io_device_nonfatal_re_n", &io_pgroup,
2N/A "serd_io_device_nonfatal_re_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_re_t", &io_pgroup,
2N/A "serd_io_device_nonfatal_re_t", maybe_di_chars_copy },
2N/A { "serd_io_device_nonfatal_rto_n", &io_pgroup,
2N/A "serd_io_device_nonfatal_rto_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_rto_t", &io_pgroup,
2N/A "serd_io_device_nonfatal_rto_t", maybe_di_chars_copy },
2N/A { "serd_io_device_nonfatal_rnr_n", &io_pgroup,
2N/A "serd_io_device_nonfatal_rnr_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_device_nonfatal_rnr_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy },
2N/A { "serd_io_pciex_corrlink-bus_btlp_n", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_btlp_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_pciex_corrlink-bus_btlp_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_btlp_t", maybe_di_chars_copy },
2N/A { "serd_io_pciex_corrlink-bus_bdllp_n", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_bdllp_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_pciex_corrlink-bus_bdllp_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_bdllp_t", maybe_di_chars_copy },
2N/A { "serd_io_pciex_corrlink-bus_re_n", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_re_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_pciex_corrlink-bus_re_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_re_t", maybe_di_chars_copy },
2N/A { "serd_io_pciex_corrlink-bus_rto_n", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_rto_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_pciex_corrlink-bus_rto_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_rto_t", maybe_di_chars_copy },
2N/A { "serd_io_pciex_corrlink-bus_rnr_n", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_rnr_n", maybe_di_uint_to_dec_str },
2N/A { "serd_io_pciex_corrlink-bus_rnr_t", &io_pgroup,
2N/A "serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy },
2N/A { NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set },
2N/A { DI_CLASSPROP, &pci_pgroup, TOPO_PCI_CLASS, maybe_di_uint_to_str },
2N/A { DI_VENDIDPROP, &pci_pgroup, TOPO_PCI_VENDID, maybe_di_uint_to_str },
2N/A { DI_AADDRPROP, &pci_pgroup, TOPO_PCI_AADDR, AADDR_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
2N/A};
2N/A
2N/Atxprop_t Dev_common_props[] = {
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
2N/A};
2N/A
2N/Atxprop_t Bus_common_props[] = {
2N/A { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
2N/A { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
2N/A { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
2N/A};
2N/A
2N/Atxprop_t RC_common_props[] = {
2N/A { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
2N/A { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
2N/A { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
2N/A { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
2N/A { NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set },
2N/A { NULL, &pci_pgroup, TOPO_PCI_BDF, BDF_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
2N/A /*
2N/A * These props need to be put at the end of table. x86pi has its
2N/A * own way to set them.
2N/A */
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
2N/A};
2N/A
2N/Atxprop_t ExHB_common_props[] = {
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
2N/A /*
2N/A * These props need to be put at the end of table. x86pi has its
2N/A * own way to set them.
2N/A */
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
2N/A};
2N/A
2N/Atxprop_t IOB_common_props[] = {
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
2N/A};
2N/A
2N/Atxprop_t HB_common_props[] = {
2N/A { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
2N/A { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
2N/A { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
2N/A /*
2N/A * These props need to be put at the end of table. x86pi has its
2N/A * own way to set them.
2N/A */
2N/A { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
2N/A { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
2N/A};
2N/A
2N/Aint Bus_propcnt = sizeof (Bus_common_props) / sizeof (txprop_t);
2N/Aint Dev_propcnt = sizeof (Dev_common_props) / sizeof (txprop_t);
2N/Aint ExHB_propcnt = sizeof (ExHB_common_props) / sizeof (txprop_t);
2N/Aint HB_propcnt = sizeof (HB_common_props) / sizeof (txprop_t);
2N/Aint IOB_propcnt = sizeof (IOB_common_props) / sizeof (txprop_t);
2N/Aint RC_propcnt = sizeof (RC_common_props) / sizeof (txprop_t);
2N/Aint Fn_propcnt = sizeof (Fn_common_props) / sizeof (txprop_t);
2N/A
2N/A/*
2N/A * If this devinfo node came originally from OBP data, we'll have prom
2N/A * properties associated with the node where we can find properties of
2N/A * interest. We ignore anything after the the first four bytes of the
2N/A * property, and interpet those first four bytes as our unsigned
2N/A * integer. If we don't find the property or it's not large enough,
2N/A * 'val' will remained unchanged and we'll return -1. Otherwise 'val'
2N/A * gets updated with the property value and we return 0.
2N/A */
2N/Astatic int
2N/Apromprop2uint(topo_mod_t *mod, di_node_t n, const char *propnm, uint_t *val)
2N/A{
2N/A di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
2N/A di_prom_prop_t pp = DI_PROM_PROP_NIL;
2N/A uchar_t *buf;
2N/A
2N/A if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
2N/A return (-1);
2N/A
2N/A while ((pp = di_prom_prop_next(ptp, n, pp)) != DI_PROM_PROP_NIL) {
2N/A if (strcmp(di_prom_prop_name(pp), propnm) == 0) {
2N/A if (di_prom_prop_data(pp, &buf) < sizeof (uint_t))
2N/A continue;
2N/A bcopy(buf, val, sizeof (uint_t));
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * If this devinfo node was added by the PCI hotplug framework it
2N/A * doesn't have the PROM properties, but hopefully has the properties
2N/A * we're looking for attached directly to the devinfo node. We only
2N/A * care about the first four bytes of the property, which we read as
2N/A * our unsigned integer. The remaining bytes are ignored. If we
2N/A * don't find the property we're looking for, or can't get its value,
2N/A * 'val' remains unchanged and we return -1. Otherwise 'val' gets the
2N/A * property value and we return 0.
2N/A */
2N/Astatic int
2N/Ahwprop2uint(di_node_t n, const char *propnm, uint_t *val)
2N/A{
2N/A di_prop_t hp = DI_PROP_NIL;
2N/A uchar_t *buf;
2N/A
2N/A while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) {
2N/A if (strcmp(di_prop_name(hp), propnm) == 0) {
2N/A if (di_prop_bytes(hp, &buf) < sizeof (uint_t))
2N/A continue;
2N/A bcopy(buf, val, sizeof (uint_t));
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Adi_uintprop_get(topo_mod_t *mod, di_node_t n, const char *pnm, uint_t *pv)
2N/A{
2N/A if (hwprop2uint(n, pnm, pv) < 0)
2N/A if (promprop2uint(mod, n, pnm, pv) < 0)
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Adi_bytes_get(topo_mod_t *mod, di_node_t n, const char *pnm, int *sz,
2N/A uchar_t **db)
2N/A{
2N/A di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
2N/A di_prom_prop_t pp = DI_PROM_PROP_NIL;
2N/A di_prop_t hp = DI_PROP_NIL;
2N/A
2N/A if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
2N/A return (-1);
2N/A
2N/A *sz = -1;
2N/A while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) {
2N/A if (strcmp(di_prop_name(hp), pnm) == 0) {
2N/A if ((*sz = di_prop_bytes(hp, db)) < 0)
2N/A continue;
2N/A break;
2N/A }
2N/A }
2N/A if (*sz < 0) {
2N/A while ((pp = di_prom_prop_next(ptp, n, pp)) !=
2N/A DI_PROM_PROP_NIL) {
2N/A if (strcmp(di_prom_prop_name(pp), pnm) == 0) {
2N/A *sz = di_prom_prop_data(pp, db);
2N/A if (*sz < 0)
2N/A continue;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (*sz < 0)
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * fix_dev_prop -- sometimes di_devfs_path() doesn't tell the whole
2N/A * story, leaving off the device and function number. Chances are if
2N/A * devfs doesn't put these on then we'll never see this device as an
2N/A * error detector called out in an ereport. Unfortunately, there are
2N/A * races and we sometimes do get ereports from devices that devfs
2N/A * decides aren't there. For example, the error injector card seems
2N/A * to bounce in and out of existence according to devfs. We tack on
2N/A * the missing dev and fn here so that the DEV property used to look
2N/A * up the topology node is correct.
2N/A */
2N/Astatic char *
2N/Adev_path_fix(topo_mod_t *mp, char *path, int devno, int fnno)
2N/A{
2N/A char *lastslash;
2N/A char *newpath;
2N/A int need;
2N/A
2N/A /*
2N/A * We only care about the last component of the dev path. If
2N/A * we don't find a slash, something is weird.
2N/A */
2N/A lastslash = strrchr(path, '/');
2N/A assert(lastslash != NULL);
2N/A
2N/A /*
2N/A * If an @ sign is present in the last component, the
2N/A * di_devfs_path() result had the device,fn unit-address.
2N/A * In that case there's nothing we need do.
2N/A */
2N/A if (strchr(lastslash, '@') != NULL)
2N/A return (path);
2N/A
2N/A if (fnno == 0)
2N/A need = snprintf(NULL, 0, "%s@%x", path, devno);
2N/A else
2N/A need = snprintf(NULL, 0, "%s@%x,%x", path, devno, fnno);
2N/A need++;
2N/A
2N/A if ((newpath = topo_mod_alloc(mp, need)) == NULL) {
2N/A topo_mod_strfree(mp, path);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (fnno == 0)
2N/A (void) snprintf(newpath, need, "%s@%x", path, devno);
2N/A else
2N/A (void) snprintf(newpath, need, "%s@%x,%x", path, devno, fnno);
2N/A
2N/A topo_mod_strfree(mp, path);
2N/A return (newpath);
2N/A}
2N/A
2N/A/*
2N/A * dev_for_hostbridge() -- For hostbridges we truncate the devfs path
2N/A * after the first element in the bus address.
2N/A */
2N/Astatic char *
2N/Adev_for_hostbridge(topo_mod_t *mp, char *path)
2N/A{
2N/A char *lastslash;
2N/A char *newpath;
2N/A char *comma;
2N/A int plen;
2N/A
2N/A plen = strlen(path) + 1;
2N/A
2N/A /*
2N/A * We only care about the last component of the dev path. If
2N/A * we don't find a slash, something is weird.
2N/A */
2N/A lastslash = strrchr(path, '/');
2N/A assert(lastslash != NULL);
2N/A
2N/A /*
2N/A * Find the comma in the last component component@x,y, and
2N/A * truncate the comma and any following number.
2N/A */
2N/A comma = strchr(lastslash, ',');
2N/A assert(comma != NULL);
2N/A
2N/A *comma = '\0';
2N/A if ((newpath = topo_mod_strdup(mp, path)) == NULL) {
2N/A topo_mod_free(mp, path, plen);
2N/A return (NULL);
2N/A }
2N/A
2N/A *comma = ',';
2N/A topo_mod_free(mp, path, plen);
2N/A return (newpath);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/AASRU_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A nvlist_t *fmri;
2N/A char *dnpath, *path, *fpath, *nm;
2N/A int d, e, f;
2N/A
2N/A /*
2N/A * If this topology node represents a function of device,
2N/A * set the ASRU to a dev scheme FMRI based on the value of
2N/A * di_devfs_path(). If that path is NULL, set the ASRU to
2N/A * be the resource describing this topology node. If this
2N/A * isn't a function, inherit any ASRU from the parent.
2N/A */
2N/A mp = did_mod(pd);
2N/A nm = topo_node_name(tn);
2N/A if ((strcmp(nm, PCI_BUS) == 0 && did_gettnode(pd) &&
2N/A strcmp(topo_node_name(did_gettnode(pd)), HOSTBRIDGE) == 0) ||
2N/A strcmp(nm, PCI_FUNCTION) == 0 || strcmp(nm, PCIEX_FUNCTION) == 0 ||
2N/A strcmp(nm, PCIEX_ROOT) == 0) {
2N/A if ((dnpath = di_devfs_path(did_dinode(pd))) != NULL) {
2N/A /*
2N/A * Dup the path, dev_path_fix() may replace it and
2N/A * dev_path_fix() wouldn't know to use
2N/A * di_devfs_path_free()
2N/A */
2N/A if ((path = topo_mod_strdup(mp, dnpath)) == NULL) {
2N/A di_devfs_path_free(dnpath);
2N/A return (topo_mod_seterrno(mp, EMOD_NOMEM));
2N/A }
2N/A di_devfs_path_free(dnpath);
2N/A did_BDF(pd, NULL, &d, &f);
2N/A if ((fpath = dev_path_fix(mp, path, d, f)) == NULL)
2N/A return (topo_mod_seterrno(mp, EMOD_NOMEM));
2N/A
2N/A fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION,
2N/A fpath, NULL);
2N/A if (fmri == NULL) {
2N/A topo_mod_dprintf(mp,
2N/A "dev:///%s fmri creation failed.\n", fpath);
2N/A topo_mod_strfree(mp, fpath);
2N/A return (-1);
2N/A }
2N/A topo_mod_strfree(mp, fpath);
2N/A } else {
2N/A topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
2N/A if (topo_prop_get_fmri(tn, TOPO_PGROUP_PROTOCOL,
2N/A TOPO_PROP_RESOURCE, &fmri, &e) < 0)
2N/A return (topo_mod_seterrno(mp, e));
2N/A }
2N/A if (topo_node_asru_set(tn, fmri, 0, &e) < 0) {
2N/A nvlist_free(fmri);
2N/A return (topo_mod_seterrno(mp, e));
2N/A }
2N/A nvlist_free(fmri);
2N/A return (0);
2N/A }
2N/A (void) topo_node_asru_set(tn, NULL, 0, &e);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Set the FRU property to the hc fmri of this tnode
2N/A */
2N/Aint
2N/AFRU_fmri_set(topo_mod_t *mp, tnode_t *tn)
2N/A{
2N/A nvlist_t *fmri;
2N/A int err, e;
2N/A
2N/A if (topo_node_resource(tn, &fmri, &err) < 0 ||
2N/A fmri == NULL) {
2N/A topo_mod_dprintf(mp, "FRU_fmri_set error: %s\n",
2N/A topo_strerror(topo_mod_errno(mp)));
2N/A return (topo_mod_seterrno(mp, err));
2N/A }
2N/A e = topo_node_fru_set(tn, fmri, 0, &err);
2N/A nvlist_free(fmri);
2N/A if (e < 0)
2N/A return (topo_mod_seterrno(mp, err));
2N/A return (0);
2N/A}
2N/A
2N/Atnode_t *
2N/Afind_predecessor(tnode_t *tn, char *mod_name)
2N/A{
2N/A tnode_t *pnode = topo_node_parent(tn);
2N/A
2N/A while (pnode && (strcmp(topo_node_name(pnode), mod_name) != 0)) {
2N/A pnode = topo_node_parent(pnode);
2N/A }
2N/A return (pnode);
2N/A}
2N/A
2N/Astatic int
2N/Ause_predecessor_fru(tnode_t *tn, char *mod_name)
2N/A{
2N/A tnode_t *pnode = NULL;
2N/A nvlist_t *fru = NULL;
2N/A int err = 0;
2N/A
2N/A if ((pnode = find_predecessor(tn, mod_name)) == NULL)
2N/A return (-1);
2N/A if ((pnode = topo_node_parent(pnode)) == NULL)
2N/A return (-1);
2N/A if (topo_node_fru(pnode, &fru, NULL, &err) != 0)
2N/A return (-1);
2N/A
2N/A (void) topo_node_fru_set(tn, fru, 0, &err);
2N/A nvlist_free(fru);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Ause_predecessor_label(topo_mod_t *mod, tnode_t *tn, char *mod_name)
2N/A{
2N/A tnode_t *pnode = NULL;
2N/A int err = 0;
2N/A char *plabel = NULL;
2N/A
2N/A if ((pnode = find_predecessor(tn, mod_name)) == NULL)
2N/A return (-1);
2N/A if ((pnode = topo_node_parent(pnode)) == NULL)
2N/A return (-1);
2N/A if (topo_node_label(pnode, &plabel, &err) != 0 || plabel == NULL)
2N/A return (-1);
2N/A
2N/A (void) topo_node_label_set(tn, plabel, &err);
2N/A
2N/A topo_mod_strfree(mod, plabel);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/AFRU_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A char *nm;
2N/A int e = 0, err = 0;
2N/A
2N/A nm = topo_node_name(tn);
2N/A mp = did_mod(pd);
2N/A
2N/A /*
2N/A * If this is a PCIEX_BUS and its parent is a PCIEX_ROOT,
2N/A * check for a CPUBOARD predecessor. If found, inherit its
2N/A * parent's FRU. Otherwise, continue with FRU set.
2N/A */
2N/A if ((strcmp(nm, PCIEX_BUS) == 0) &&
2N/A (strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) {
2N/A
2N/A if (use_predecessor_fru(tn, CPUBOARD) == 0)
2N/A return (0);
2N/A }
2N/A /*
2N/A * If this topology node represents something other than an
2N/A * ioboard or a device that implements a slot, inherit the
2N/A * parent's FRU value. If there is no label, inherit our
2N/A * parent's FRU value. Otherwise, munge up an fmri based on
2N/A * the label.
2N/A */
2N/A if (strcmp(nm, IOBOARD) != 0 && strcmp(nm, PCI_DEVICE) != 0 &&
2N/A strcmp(nm, PCIEX_DEVICE) != 0 && strcmp(nm, PCIEX_BUS) != 0) {
2N/A (void) topo_node_fru_set(tn, NULL, 0, &e);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * If ioboard, set fru fmri to hc fmri
2N/A */
2N/A if (strcmp(nm, IOBOARD) == 0) {
2N/A e = FRU_fmri_set(mp, tn);
2N/A return (e);
2N/A } else if (strcmp(nm, PCI_DEVICE) == 0 ||
2N/A strcmp(nm, PCIEX_DEVICE) == 0 || strcmp(nm, PCIEX_BUS) == 0) {
2N/A nvlist_t *in, *out;
2N/A
2N/A mp = did_mod(pd);
2N/A if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0)
2N/A return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
2N/A if (nvlist_add_uint64(in, "nv1", (uintptr_t)pd) != 0) {
2N/A nvlist_free(in);
2N/A return (topo_mod_seterrno(mp, EMOD_NOMEM));
2N/A }
2N/A if (topo_method_invoke(tn,
2N/A TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_VERSION,
2N/A in, &out, &err) != 0) {
2N/A nvlist_free(in);
2N/A return (topo_mod_seterrno(mp, err));
2N/A }
2N/A nvlist_free(in);
2N/A (void) topo_node_fru_set(tn, out, 0, &err);
2N/A if (out != NULL)
2N/A nvlist_free(out);
2N/A } else
2N/A (void) topo_node_fru_set(tn, NULL, 0, &err);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Alabel_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A nvlist_t *in, *out;
2N/A char *label;
2N/A int err;
2N/A
2N/A mp = did_mod(pd);
2N/A /*
2N/A * If this is a PCIEX_BUS and its parent is a PCIEX_ROOT,
2N/A * check for a CPUBOARD predecessor. If found, inherit its
2N/A * parent's Label. Otherwise, continue with label set.
2N/A */
2N/A if ((strcmp(topo_node_name(tn), PCIEX_BUS) == 0) &&
2N/A (strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) {
2N/A
2N/A if (use_predecessor_label(mp, tn, CPUBOARD) == 0)
2N/A return (0);
2N/A }
2N/A if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0)
2N/A return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
2N/A if (nvlist_add_uint64(in, TOPO_METH_LABEL_ARG_NVL, (uintptr_t)pd) !=
2N/A 0) {
2N/A nvlist_free(in);
2N/A return (topo_mod_seterrno(mp, EMOD_NOMEM));
2N/A }
2N/A if (topo_method_invoke(tn,
2N/A TOPO_METH_LABEL, TOPO_METH_LABEL_VERSION, in, &out, &err) != 0) {
2N/A nvlist_free(in);
2N/A return (topo_mod_seterrno(mp, err));
2N/A }
2N/A nvlist_free(in);
2N/A if (out != NULL &&
2N/A nvlist_lookup_string(out, TOPO_METH_LABEL_RET_STR, &label) == 0) {
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_PROTOCOL,
2N/A TOPO_PROP_LABEL, TOPO_PROP_IMMUTABLE, label, &err) != 0) {
2N/A nvlist_free(out);
2N/A return (topo_mod_seterrno(mp, err));
2N/A }
2N/A nvlist_free(out);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/AEXCAP_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A int excap = did_excap(pd);
2N/A int err;
2N/A int e = 0;
2N/A
2N/A switch (excap & PCIE_PCIECAP_DEV_TYPE_MASK) {
2N/A case PCIE_PCIECAP_DEV_TYPE_ROOT:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err);
2N/A break;
2N/A case PCIE_PCIECAP_DEV_TYPE_UP:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWUP, &err);
2N/A break;
2N/A case PCIE_PCIECAP_DEV_TYPE_DOWN:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWDWN, &err);
2N/A break;
2N/A case PCIE_PCIECAP_DEV_TYPE_PCI2PCIE:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_BUS, &err);
2N/A break;
2N/A case PCIE_PCIECAP_DEV_TYPE_PCIE2PCI:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCI_BUS, &err);
2N/A break;
2N/A case PCIE_PCIECAP_DEV_TYPE_PCIE_DEV:
2N/A e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
2N/A TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_DEVICE, &err);
2N/A break;
2N/A }
2N/A if (e != 0)
2N/A return (topo_mod_seterrno(did_mod(pd), err));
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/ADEVprop_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A char *dnpath;
2N/A char *path, *fpath;
2N/A int d, f;
2N/A int err, e;
2N/A
2N/A mp = did_mod(pd);
2N/A dnpath = di_devfs_path(did_dinode(pd));
2N/A if ((dnpath == NULL) || (strstr(dnpath, "/pci@") == NULL)) {
2N/A topo_mod_dprintf(mp, "%s di_devfs_path.\n",
2N/A dnpath == NULL ? "NULL" : "BAD");
2N/A if (dnpath != NULL)
2N/A di_devfs_path_free(dnpath);
2N/A return (topo_mod_seterrno(mp, ETOPO_PROP_NOENT));
2N/A }
2N/A if ((path = topo_mod_strdup(mp, dnpath)) == NULL) {
2N/A di_devfs_path_free(dnpath);
2N/A return (-1);
2N/A }
2N/A di_devfs_path_free(dnpath);
2N/A
2N/A /* The DEV path is modified for hostbridges */
2N/A if (strcmp(topo_node_name(tn), HOSTBRIDGE) == 0) {
2N/A fpath = dev_for_hostbridge(did_mod(pd), path);
2N/A } else {
2N/A did_BDF(pd, NULL, &d, &f);
2N/A fpath = dev_path_fix(mp, path, d, f);
2N/A }
2N/A if (fpath == NULL)
2N/A return (-1);
2N/A e = topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, fpath, &err);
2N/A topo_mod_strfree(mp, fpath);
2N/A if (e != 0)
2N/A return (topo_mod_seterrno(mp, err));
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/ADRIVERprop_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A char *dnm;
2N/A int err;
2N/A
2N/A if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
2N/A return (0);
2N/A if (topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, dnm, &err) < 0)
2N/A return (topo_mod_seterrno(did_mod(pd), err));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/AMODULEprop_set(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A nvlist_t *mod;
2N/A topo_mod_t *mp;
2N/A char *dnm;
2N/A int err;
2N/A
2N/A if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
2N/A return (0);
2N/A
2N/A mp = did_mod(pd);
2N/A if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, dnm)) == NULL)
2N/A return (0); /* driver maybe detached, return success */
2N/A
2N/A if (topo_prop_set_fmri(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, mod,
2N/A &err) < 0) {
2N/A nvlist_free(mod);
2N/A return (topo_mod_seterrno(mp, err));
2N/A }
2N/A nvlist_free(mod);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Amaybe_di_chars_copy(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A uchar_t *typbuf;
2N/A char *tmpbuf;
2N/A int sz = -1;
2N/A int err, e;
2N/A
2N/A if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
2N/A return (0);
2N/A mp = did_mod(pd);
2N/A
2N/A if ((tmpbuf = topo_mod_alloc(mp, sz + 1)) == NULL)
2N/A return (topo_mod_seterrno(mp, EMOD_NOMEM));
2N/A
2N/A bcopy(typbuf, tmpbuf, sz);
2N/A tmpbuf[sz] = 0;
2N/A e = topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, tmpbuf, &err);
2N/A topo_mod_free(mp, tmpbuf, sz + 1);
2N/A if (e != 0)
2N/A return (topo_mod_seterrno(mp, err));
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Auint_to_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
2N/A const char *tpgrp, const char *tpnm)
2N/A{
2N/A char str[21]; /* sizeof (UINT64_MAX) + '\0' */
2N/A int e;
2N/A
2N/A (void) snprintf(str, 21, "%x", v);
2N/A if (topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
2N/A return (topo_mod_seterrno(mp, e));
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Amaybe_di_uint_to_str(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A uint_t v;
2N/A
2N/A if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
2N/A return (0);
2N/A
2N/A return (uint_to_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
2N/A}
2N/A
2N/Astatic int
2N/Auint_to_dec_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
2N/A const char *tpgrp, const char *tpnm)
2N/A{
2N/A char str[21]; /* sizeof (UINT64_MAX) + '\0' */
2N/A int e;
2N/A
2N/A (void) snprintf(str, 21, "%d", v);
2N/A if (topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
2N/A return (topo_mod_seterrno(mp, e));
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Amaybe_di_uint_to_dec_str(tnode_t *tn, did_t *pd,
2N/A const char *dpnm, const char *tpgrp, const char *tpnm)
2N/A{
2N/A uint_t v;
2N/A
2N/A if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
2N/A return (0);
2N/A
2N/A return (uint_to_dec_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
2N/A}
2N/A
2N/Astatic int
2N/AAADDR_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
2N/A const char *tpnm)
2N/A{
2N/A topo_mod_t *mp;
2N/A uchar_t *typbuf;
2N/A int sz = -1;
2N/A int err, e;
2N/A
2N/A if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
2N/A return (0);
2N/A
2N/A mp = did_mod(pd);
2N/A
2N/A e = topo_prop_set_uint32_array(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE,
2N/A /*LINTED*/
2N/A (uint32_t *)typbuf, sz/4, &err);
2N/A
2N/A if (e != 0)
2N/A return (topo_mod_seterrno(mp, err));
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/ABDF_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
2N/A const char *tpnm)
2N/A{
2N/A int bdf;
2N/A char str[23]; /* '0x' + sizeof (UINT64_MAX) + '\0' */
2N/A int e;
2N/A
2N/A if ((bdf = did_bdf(pd)) <= 0)
2N/A return (0);
2N/A
2N/A (void) snprintf(str, 23, "0x%x", bdf);
2N/A if (topo_prop_set_string(tn,
2N/A tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
2N/A return (topo_mod_seterrno(did_mod(pd), e));
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Adid_props_set(tnode_t *tn, did_t *pd, txprop_t txarray[], int txnum)
2N/A{
2N/A topo_mod_t *mp;
2N/A int i, r, e;
2N/A
2N/A mp = did_mod(pd);
2N/A for (i = 0; i < txnum; i++) {
2N/A /*
2N/A * Ensure the property group has been created.
2N/A */
2N/A if (txarray[i].tx_tpgroup != NULL) {
2N/A if (topo_pgroup_create(tn, txarray[i].tx_tpgroup, &e)
2N/A < 0) {
2N/A if (e != ETOPO_PROP_DEFD)
2N/A return (topo_mod_seterrno(mp, e));
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mp,
2N/A "Setting property %s in group %s.\n",
2N/A txarray[i].tx_tprop, txarray[i].tx_tpgroup->tpi_name);
2N/A r = txarray[i].tx_xlate(tn, pd,
2N/A txarray[i].tx_diprop, txarray[i].tx_tpgroup->tpi_name,
2N/A txarray[i].tx_tprop);
2N/A if (r != 0) {
2N/A topo_mod_dprintf(mp, "failed.\n");
2N/A topo_mod_dprintf(mp, "Error was %s.\n",
2N/A topo_strerror(topo_mod_errno(mp)));
2N/A return (-1);
2N/A }
2N/A topo_mod_dprintf(mp, "succeeded.\n");
2N/A }
2N/A return (0);
2N/A}