#include <assert.h>
#include <alloca.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/pci.h>
#include <sys/pcie.h>
#include <sys/fm/protocol.h>
#include <fm/topo_mod.h>
#include <fm/topo_hc.h>
#include <libdevinfo.h>
#include <hostbridge.h>
#include <pcibus.h>
#include <did.h>
#include <did_props.h>
#include <fm/libtopo.h>
#include <pcidb.h>
static int ASRU_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int FRU_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int DEVprop_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int DRIVERprop_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int INSTprop_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int MODULEprop_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int EXCAP_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int BDF_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int label_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_di_chars_copy(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_di_uint_to_str(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_di_uint_to_dec_str(tnode_t *, did_t *,
const char *, const char *, const char *);
static int AADDR_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_pcidb_set(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_di_int_to_uint32(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_pcie_speed(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_pcie_supported_speed(tnode_t *, did_t *,
const char *, const char *, const char *);
static int maybe_pcie_target_speed(tnode_t *, did_t *,
const char *, const char *, const char *);
static const topo_pgroup_info_t io_pgroup =
{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t pci_pgroup =
{ TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
static const topo_pgroup_info_t protocol_pgroup = {
TOPO_PGROUP_PROTOCOL,
TOPO_STABILITY_PRIVATE,
TOPO_STABILITY_PRIVATE,
1
};
txprop_t Fn_common_props[] = {
{ NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
{ DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
{ DI_DEVIDPROP, &pci_pgroup, TOPO_PCI_DEVID, maybe_di_uint_to_str },
{ NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
{ NULL, &io_pgroup, TOPO_IO_INSTANCE, INSTprop_set },
{ NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
{ "serd_io_device_nonfatal_n", &io_pgroup, "serd_io_device_nonfatal_n",
maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_t", &io_pgroup, "serd_io_device_nonfatal_t",
maybe_di_chars_copy },
{ "serd_io_device_nonfatal_btlp_n", &io_pgroup,
"serd_io_device_nonfatal_btlp_n", maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_btlp_t", &io_pgroup,
"serd_io_device_nonfatal_btlp_t", maybe_di_chars_copy },
{ "serd_io_device_nonfatal_bdllp_n", &io_pgroup,
"serd_io_device_nonfatal_bdllp_n", maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_bdllp_t", &io_pgroup,
"serd_io_device_nonfatal_bdllp_t", maybe_di_chars_copy },
{ "serd_io_device_nonfatal_re_n", &io_pgroup,
"serd_io_device_nonfatal_re_n", maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_re_t", &io_pgroup,
"serd_io_device_nonfatal_re_t", maybe_di_chars_copy },
{ "serd_io_device_nonfatal_rto_n", &io_pgroup,
"serd_io_device_nonfatal_rto_n", maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_rto_t", &io_pgroup,
"serd_io_device_nonfatal_rto_t", maybe_di_chars_copy },
{ "serd_io_device_nonfatal_rnr_n", &io_pgroup,
"serd_io_device_nonfatal_rnr_n", maybe_di_uint_to_dec_str },
{ "serd_io_device_nonfatal_rnr_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy },
{ "serd_io_pciex_corrlink-bus_btlp_n", &io_pgroup,
"serd_io_pciex_corrlink-bus_btlp_n", maybe_di_uint_to_dec_str },
{ "serd_io_pciex_corrlink-bus_btlp_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_btlp_t", maybe_di_chars_copy },
{ "serd_io_pciex_corrlink-bus_bdllp_n", &io_pgroup,
"serd_io_pciex_corrlink-bus_bdllp_n", maybe_di_uint_to_dec_str },
{ "serd_io_pciex_corrlink-bus_bdllp_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_bdllp_t", maybe_di_chars_copy },
{ "serd_io_pciex_corrlink-bus_re_n", &io_pgroup,
"serd_io_pciex_corrlink-bus_re_n", maybe_di_uint_to_dec_str },
{ "serd_io_pciex_corrlink-bus_re_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_re_t", maybe_di_chars_copy },
{ "serd_io_pciex_corrlink-bus_rto_n", &io_pgroup,
"serd_io_pciex_corrlink-bus_rto_n", maybe_di_uint_to_dec_str },
{ "serd_io_pciex_corrlink-bus_rto_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_rto_t", maybe_di_chars_copy },
{ "serd_io_pciex_corrlink-bus_rnr_n", &io_pgroup,
"serd_io_pciex_corrlink-bus_rnr_n", maybe_di_uint_to_dec_str },
{ "serd_io_pciex_corrlink-bus_rnr_t", &io_pgroup,
"serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy },
{ NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set },
{ DI_CLASSPROP, &pci_pgroup, TOPO_PCI_CLASS, maybe_di_uint_to_str },
{ DI_VENDIDPROP, &pci_pgroup, TOPO_PCI_VENDID, maybe_di_uint_to_str },
{ DI_AADDRPROP, &pci_pgroup, TOPO_PCI_AADDR, AADDR_set },
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
{ NULL, &pci_pgroup, NULL, maybe_pcidb_set }
};
txprop_t Dev_common_props[] = {
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
{ DI_PCIE_MAX_WIDTH, &pci_pgroup, TOPO_PCI_MAX_WIDTH,
maybe_di_int_to_uint32 },
{ DI_PCIE_CUR_WIDTH, &pci_pgroup, TOPO_PCI_CUR_WIDTH,
maybe_di_int_to_uint32 },
{ DI_PCIE_MAX_SPEED, &pci_pgroup, TOPO_PCI_MAX_SPEED,
maybe_pcie_speed },
{ DI_PCIE_CUR_SPEED, &pci_pgroup, TOPO_PCI_CUR_SPEED,
maybe_pcie_speed },
{ DI_PCIE_SUP_SPEEDS, &pci_pgroup, TOPO_PCI_SUP_SPEED,
maybe_pcie_supported_speed },
{ NULL, &pci_pgroup, TOPO_PCI_ADMIN_SPEED, maybe_pcie_target_speed }
};
txprop_t Bus_common_props[] = {
{ DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
{ NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
{ NULL, &io_pgroup, TOPO_IO_INSTANCE, INSTprop_set },
{ NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
};
txprop_t RC_common_props[] = {
{ NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
{ DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy },
{ NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
{ NULL, &io_pgroup, TOPO_IO_INSTANCE, INSTprop_set },
{ NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
{ NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set },
{ NULL, &pci_pgroup, TOPO_PCI_BDF, BDF_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
};
txprop_t ExHB_common_props[] = {
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
};
txprop_t IOB_common_props[] = {
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }
};
txprop_t HB_common_props[] = {
{ NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set },
{ NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set },
{ NULL, &io_pgroup, TOPO_IO_INSTANCE, INSTprop_set },
{ NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set },
{ NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set },
{ NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set },
{ NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }
};
int Bus_propcnt = sizeof (Bus_common_props) / sizeof (txprop_t);
int Dev_propcnt = sizeof (Dev_common_props) / sizeof (txprop_t);
int ExHB_propcnt = sizeof (ExHB_common_props) / sizeof (txprop_t);
int HB_propcnt = sizeof (HB_common_props) / sizeof (txprop_t);
int IOB_propcnt = sizeof (IOB_common_props) / sizeof (txprop_t);
int RC_propcnt = sizeof (RC_common_props) / sizeof (txprop_t);
int Fn_propcnt = sizeof (Fn_common_props) / sizeof (txprop_t);
static int
promprop2uint(topo_mod_t *mod, di_node_t n, const char *propnm, uint_t *val)
{
di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
di_prom_prop_t pp = DI_PROM_PROP_NIL;
uchar_t *buf;
if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
return (-1);
while ((pp = di_prom_prop_next(ptp, n, pp)) != DI_PROM_PROP_NIL) {
if (strcmp(di_prom_prop_name(pp), propnm) == 0) {
if (di_prom_prop_data(pp, &buf) < sizeof (uint_t))
continue;
bcopy(buf, val, sizeof (uint_t));
return (0);
}
}
return (-1);
}
static int
hwprop2uint(di_node_t n, const char *propnm, uint_t *val)
{
di_prop_t hp = DI_PROP_NIL;
uchar_t *buf;
while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) {
if (strcmp(di_prop_name(hp), propnm) == 0) {
if (di_prop_bytes(hp, &buf) < sizeof (uint_t))
continue;
bcopy(buf, val, sizeof (uint_t));
return (0);
}
}
return (-1);
}
int
di_uintprop_get(topo_mod_t *mod, di_node_t n, const char *pnm, uint_t *pv)
{
if (hwprop2uint(n, pnm, pv) < 0)
if (promprop2uint(mod, n, pnm, pv) < 0)
return (-1);
return (0);
}
int
di_bytes_get(topo_mod_t *mod, di_node_t n, const char *pnm, int *sz,
uchar_t **db)
{
di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
di_prom_prop_t pp = DI_PROM_PROP_NIL;
di_prop_t hp = DI_PROP_NIL;
if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL)
return (-1);
*sz = -1;
while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) {
if (strcmp(di_prop_name(hp), pnm) == 0) {
if ((*sz = di_prop_bytes(hp, db)) < 0)
continue;
break;
}
}
if (*sz < 0) {
while ((pp = di_prom_prop_next(ptp, n, pp)) !=
DI_PROM_PROP_NIL) {
if (strcmp(di_prom_prop_name(pp), pnm) == 0) {
*sz = di_prom_prop_data(pp, db);
if (*sz < 0)
continue;
break;
}
}
}
if (*sz < 0)
return (-1);
return (0);
}
static char *
dev_path_fix(topo_mod_t *mp, char *path, int devno, int fnno)
{
char *lastslash;
char *newpath;
int need;
lastslash = strrchr(path, '/');
assert(lastslash != NULL);
if (strchr(lastslash, '@') != NULL)
return (path);
if (fnno == 0)
need = snprintf(NULL, 0, "%s@%x", path, devno);
else
need = snprintf(NULL, 0, "%s@%x,%x", path, devno, fnno);
need++;
if ((newpath = topo_mod_alloc(mp, need)) == NULL) {
topo_mod_strfree(mp, path);
return (NULL);
}
if (fnno == 0)
(void) snprintf(newpath, need, "%s@%x", path, devno);
else
(void) snprintf(newpath, need, "%s@%x,%x", path, devno, fnno);
topo_mod_strfree(mp, path);
return (newpath);
}
static char *
dev_for_hostbridge(topo_mod_t *mp, char *path)
{
char *lastslash;
char *newpath;
char *comma;
int plen;
plen = strlen(path) + 1;
lastslash = strrchr(path, '/');
assert(lastslash != NULL);
comma = strchr(lastslash, ',');
assert(comma != NULL);
*comma = '\0';
if ((newpath = topo_mod_strdup(mp, path)) == NULL) {
topo_mod_free(mp, path, plen);
return (NULL);
}
*comma = ',';
topo_mod_free(mp, path, plen);
return (newpath);
}
static int
ASRU_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
topo_mod_t *mp;
nvlist_t *fmri;
char *dnpath, *path, *fpath, *nm;
int d, e, f;
mp = did_mod(pd);
nm = topo_node_name(tn);
if ((strcmp(nm, PCI_BUS) == 0 && did_gettnode(pd) &&
strcmp(topo_node_name(did_gettnode(pd)), HOSTBRIDGE) == 0) ||
strcmp(nm, PCI_FUNCTION) == 0 || strcmp(nm, PCIEX_FUNCTION) == 0 ||
strcmp(nm, PCIEX_ROOT) == 0) {
if ((dnpath = di_devfs_path(did_dinode(pd))) != NULL) {
if ((path = topo_mod_strdup(mp, dnpath)) == NULL) {
di_devfs_path_free(dnpath);
return (topo_mod_seterrno(mp, EMOD_NOMEM));
}
di_devfs_path_free(dnpath);
did_BDF(pd, NULL, &d, &f);
if ((fpath = dev_path_fix(mp, path, d, f)) == NULL)
return (topo_mod_seterrno(mp, EMOD_NOMEM));
fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION,
fpath, NULL);
if (fmri == NULL) {
topo_mod_dprintf(mp,
"dev:///%s fmri creation failed.\n", fpath);
topo_mod_strfree(mp, fpath);
return (-1);
}
topo_mod_strfree(mp, fpath);
} else {
topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
if (topo_prop_get_fmri(tn, TOPO_PGROUP_PROTOCOL,
TOPO_PROP_RESOURCE, &fmri, &e) < 0)
return (topo_mod_seterrno(mp, e));
}
if (topo_node_asru_set(tn, fmri, 0, &e) < 0) {
nvlist_free(fmri);
return (topo_mod_seterrno(mp, e));
}
nvlist_free(fmri);
return (0);
}
(void) topo_node_asru_set(tn, NULL, 0, &e);
return (0);
}
int
FRU_fmri_set(topo_mod_t *mp, tnode_t *tn)
{
nvlist_t *fmri;
int err, e;
if (topo_node_resource(tn, &fmri, &err) < 0 ||
fmri == NULL) {
topo_mod_dprintf(mp, "FRU_fmri_set error: %s\n",
topo_strerror(topo_mod_errno(mp)));
return (topo_mod_seterrno(mp, err));
}
e = topo_node_fru_set(tn, fmri, 0, &err);
nvlist_free(fmri);
if (e < 0)
return (topo_mod_seterrno(mp, err));
return (0);
}
tnode_t *
find_predecessor(tnode_t *tn, char *mod_name)
{
tnode_t *pnode = topo_node_parent(tn);
while (pnode && (strcmp(topo_node_name(pnode), mod_name) != 0)) {
pnode = topo_node_parent(pnode);
}
return (pnode);
}
static int
use_predecessor_fru(tnode_t *tn, char *mod_name)
{
tnode_t *pnode = NULL;
nvlist_t *fru = NULL;
int err = 0;
if ((pnode = find_predecessor(tn, mod_name)) == NULL)
return (-1);
if ((pnode = topo_node_parent(pnode)) == NULL)
return (-1);
if (topo_node_fru(pnode, &fru, NULL, &err) != 0)
return (-1);
(void) topo_node_fru_set(tn, fru, 0, &err);
nvlist_free(fru);
return (0);
}
static int
use_predecessor_label(topo_mod_t *mod, tnode_t *tn, char *mod_name)
{
tnode_t *pnode = NULL;
int err = 0;
char *plabel = NULL;
if ((pnode = find_predecessor(tn, mod_name)) == NULL)
return (-1);
if ((pnode = topo_node_parent(pnode)) == NULL)
return (-1);
if (topo_node_label(pnode, &plabel, &err) != 0 || plabel == NULL)
return (-1);
(void) topo_node_label_set(tn, plabel, &err);
topo_mod_strfree(mod, plabel);
return (0);
}
static int
FRU_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
topo_mod_t *mp;
char *nm;
int e = 0, err = 0;
nm = topo_node_name(tn);
mp = did_mod(pd);
if ((strcmp(nm, PCIEX_BUS) == 0) &&
(strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) {
if (use_predecessor_fru(tn, CPUBOARD) == 0)
return (0);
}
if (strcmp(nm, IOBOARD) != 0 && strcmp(nm, PCI_DEVICE) != 0 &&
strcmp(nm, PCIEX_DEVICE) != 0 && strcmp(nm, PCIEX_BUS) != 0) {
(void) topo_node_fru_set(tn, NULL, 0, &e);
return (0);
}
if (strcmp(nm, IOBOARD) == 0) {
e = FRU_fmri_set(mp, tn);
return (e);
} else if (strcmp(nm, PCI_DEVICE) == 0 ||
strcmp(nm, PCIEX_DEVICE) == 0 || strcmp(nm, PCIEX_BUS) == 0) {
nvlist_t *in, *out;
mp = did_mod(pd);
if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0)
return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
if (nvlist_add_uint64(in, "nv1", (uintptr_t)pd) != 0) {
nvlist_free(in);
return (topo_mod_seterrno(mp, EMOD_NOMEM));
}
if (topo_method_invoke(tn,
TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_VERSION,
in, &out, &err) != 0) {
nvlist_free(in);
return (topo_mod_seterrno(mp, err));
}
nvlist_free(in);
(void) topo_node_fru_set(tn, out, 0, &err);
nvlist_free(out);
} else
(void) topo_node_fru_set(tn, NULL, 0, &err);
return (0);
}
static int
label_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
topo_mod_t *mp;
nvlist_t *in, *out;
char *label;
int err;
mp = did_mod(pd);
if ((strcmp(topo_node_name(tn), PCIEX_BUS) == 0) &&
(strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) {
if (use_predecessor_label(mp, tn, CPUBOARD) == 0)
return (0);
}
if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0)
return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
if (nvlist_add_uint64(in, TOPO_METH_LABEL_ARG_NVL, (uintptr_t)pd) !=
0) {
nvlist_free(in);
return (topo_mod_seterrno(mp, EMOD_NOMEM));
}
if (topo_method_invoke(tn,
TOPO_METH_LABEL, TOPO_METH_LABEL_VERSION, in, &out, &err) != 0) {
nvlist_free(in);
return (topo_mod_seterrno(mp, err));
}
nvlist_free(in);
if (out != NULL &&
nvlist_lookup_string(out, TOPO_METH_LABEL_RET_STR, &label) == 0) {
if (topo_prop_set_string(tn, TOPO_PGROUP_PROTOCOL,
TOPO_PROP_LABEL, TOPO_PROP_IMMUTABLE, label, &err) != 0) {
nvlist_free(out);
return (topo_mod_seterrno(mp, err));
}
nvlist_free(out);
}
return (0);
}
static int
EXCAP_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
int excap = did_excap(pd);
int err;
int e = 0;
switch (excap & PCIE_PCIECAP_DEV_TYPE_MASK) {
case PCIE_PCIECAP_DEV_TYPE_ROOT:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err);
break;
case PCIE_PCIECAP_DEV_TYPE_UP:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWUP, &err);
break;
case PCIE_PCIECAP_DEV_TYPE_DOWN:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWDWN, &err);
break;
case PCIE_PCIECAP_DEV_TYPE_PCI2PCIE:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_BUS, &err);
break;
case PCIE_PCIECAP_DEV_TYPE_PCIE2PCI:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCI_BUS, &err);
break;
case PCIE_PCIECAP_DEV_TYPE_PCIE_DEV:
e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_DEVICE, &err);
break;
}
if (e != 0)
return (topo_mod_seterrno(did_mod(pd), err));
return (0);
}
static int
DEVprop_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
topo_mod_t *mp;
char *dnpath;
char *path, *fpath;
int d, f;
int err, e;
mp = did_mod(pd);
if ((dnpath = di_devfs_path(did_dinode(pd))) == NULL) {
topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
return (topo_mod_seterrno(mp, ETOPO_PROP_NOENT));
}
if ((path = topo_mod_strdup(mp, dnpath)) == NULL) {
di_devfs_path_free(dnpath);
return (-1);
}
di_devfs_path_free(dnpath);
if (strcmp(topo_node_name(tn), HOSTBRIDGE) == 0) {
fpath = dev_for_hostbridge(did_mod(pd), path);
} else {
did_BDF(pd, NULL, &d, &f);
fpath = dev_path_fix(mp, path, d, f);
}
if (fpath == NULL)
return (-1);
e = topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, fpath, &err);
topo_mod_strfree(mp, fpath);
if (e != 0)
return (topo_mod_seterrno(mp, err));
return (0);
}
static int
DRIVERprop_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
char *dnm;
int err;
if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
return (0);
if (topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, dnm, &err) < 0)
return (topo_mod_seterrno(did_mod(pd), err));
return (0);
}
static int
INSTprop_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
int inst, err;
if ((inst = di_instance(did_dinode(pd))) == -1)
return (0);
if (topo_prop_set_uint32(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, inst, &err) < 0)
return (topo_mod_seterrno(did_mod(pd), err));
return (0);
}
static int
MODULEprop_set(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
nvlist_t *mod;
topo_mod_t *mp;
char *dnm;
int err;
if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
return (0);
mp = did_mod(pd);
if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, dnm)) == NULL)
return (0);
if (topo_prop_set_fmri(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, mod,
&err) < 0) {
nvlist_free(mod);
return (topo_mod_seterrno(mp, err));
}
nvlist_free(mod);
return (0);
}
static int
maybe_di_chars_copy(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
topo_mod_t *mp;
uchar_t *typbuf;
char *tmpbuf;
int sz = -1;
int err, e;
if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
return (0);
mp = did_mod(pd);
if ((tmpbuf = topo_mod_alloc(mp, sz + 1)) == NULL)
return (topo_mod_seterrno(mp, EMOD_NOMEM));
bcopy(typbuf, tmpbuf, sz);
tmpbuf[sz] = 0;
e = topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, tmpbuf, &err);
topo_mod_free(mp, tmpbuf, sz + 1);
if (e != 0)
return (topo_mod_seterrno(mp, err));
return (0);
}
static int
uint_to_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
const char *tpgrp, const char *tpnm)
{
char str[21];
int e;
(void) snprintf(str, 21, "%x", v);
if (topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
return (topo_mod_seterrno(mp, e));
return (0);
}
static int
maybe_di_uint_to_str(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
uint_t v;
if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
return (0);
return (uint_to_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
}
static int
uint_to_dec_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
const char *tpgrp, const char *tpnm)
{
char str[21];
int e;
(void) snprintf(str, 21, "%d", v);
if (topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
return (topo_mod_seterrno(mp, e));
return (0);
}
static int
maybe_di_uint_to_dec_str(tnode_t *tn, did_t *pd,
const char *dpnm, const char *tpgrp, const char *tpnm)
{
uint_t v;
if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
return (0);
return (uint_to_dec_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
}
static int
AADDR_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
const char *tpnm)
{
topo_mod_t *mp;
uchar_t *typbuf;
int sz = -1;
int err, e;
if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
return (0);
mp = did_mod(pd);
e = topo_prop_set_uint32_array(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE,
(uint32_t *)typbuf, sz/4, &err);
if (e != 0)
return (topo_mod_seterrno(mp, err));
return (0);
}
static int
BDF_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
const char *tpnm)
{
int bdf;
char str[23];
int e;
if ((bdf = did_bdf(pd)) <= 0)
return (0);
(void) snprintf(str, 23, "0x%x", bdf);
if (topo_prop_set_string(tn,
tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
return (topo_mod_seterrno(did_mod(pd), e));
return (0);
}
static int
maybe_pcidb_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
const char *tpnm)
{
const char *vname, *dname = NULL, *ssname = NULL;
uint_t vid, pid, svid, ssid;
pcidb_vendor_t *pciv;
pcidb_device_t *pcid;
pcidb_subvd_t *pcis = NULL;
pcidb_hdl_t *pcih;
topo_mod_t *mod = did_mod(pd);
int err;
if (di_uintprop_get(did_mod(pd), did_dinode(pd), DI_VENDIDPROP, &vid) <
0 || di_uintprop_get(did_mod(pd), did_dinode(pd), DI_DEVIDPROP,
&pid) < 0) {
return (0);
}
if ((pcih = topo_mod_pcidb(mod)) == NULL ||
(pciv = pcidb_lookup_vendor(pcih, vid)) == NULL) {
return (0);
}
vname = pcidb_vendor_name(pciv);
if (vname != NULL &&
topo_prop_set_string(tn, tpgrp, TOPO_PCI_VENDNM,
TOPO_PROP_IMMUTABLE, vname, &err) != 0) {
return (topo_mod_seterrno(mod, err));
}
if ((pcid = pcidb_lookup_device_by_vendor(pciv, pid)) != NULL) {
dname = pcidb_device_name(pcid);
}
if (dname != NULL &&
topo_prop_set_string(tn, tpgrp, TOPO_PCI_DEVNM,
TOPO_PROP_IMMUTABLE, dname, &err) != 0) {
return (topo_mod_seterrno(mod, err));
}
if (pcid != NULL &&
di_uintprop_get(did_mod(pd), did_dinode(pd), DI_SUBVENDIDPROP,
&svid) == 0 &&
di_uintprop_get(did_mod(pd), did_dinode(pd), DI_SUBSYSTEMID,
&ssid) == 0) {
pcis = pcidb_lookup_subvd_by_device(pcid, svid, ssid);
}
if (pcis != NULL) {
ssname = pcidb_subvd_name(pcis);
}
if (ssname != NULL && strlen(ssname) > 0 &&
topo_prop_set_string(tn, tpgrp, TOPO_PCI_SUBSYSNM,
TOPO_PROP_IMMUTABLE, ssname, &err) != 0) {
return (topo_mod_seterrno(mod, err));
}
return (0);
}
int
did_props_set(tnode_t *tn, did_t *pd, txprop_t txarray[], int txnum)
{
topo_mod_t *mp;
int i, r, e;
mp = did_mod(pd);
for (i = 0; i < txnum; i++) {
if (txarray[i].tx_tpgroup != NULL) {
if (topo_pgroup_create(tn, txarray[i].tx_tpgroup, &e)
< 0) {
if (e != ETOPO_PROP_DEFD)
return (topo_mod_seterrno(mp, e));
}
}
topo_mod_dprintf(mp,
"Setting property %s in group %s.\n",
txarray[i].tx_tprop, txarray[i].tx_tpgroup->tpi_name);
r = txarray[i].tx_xlate(tn, pd,
txarray[i].tx_diprop, txarray[i].tx_tpgroup->tpi_name,
txarray[i].tx_tprop);
if (r != 0) {
topo_mod_dprintf(mp, "failed.\n");
topo_mod_dprintf(mp, "Error was %s.\n",
topo_strerror(topo_mod_errno(mp)));
return (-1);
}
topo_mod_dprintf(mp, "succeeded.\n");
}
return (0);
}
static int
maybe_di_int_to_uint32(tnode_t *tn, did_t *pd, const char *dpnm,
const char *tpgrp, const char *tpnm)
{
int ret, *vals;
ret = di_prop_lookup_ints(DDI_DEV_T_ANY, did_dinode(pd), dpnm, &vals);
if (ret != 1) {
return (0);
}
if (topo_prop_set_uint32(tn, tpgrp, tpnm, 0, (uint32_t)*vals, &ret) !=
0) {
return (topo_mod_seterrno(did_mod(pd), ret));
}
return (0);
}
static int
maybe_pcie_speed(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
const char *tpnm)
{
int ret;
int64_t *vals;
ret = di_prop_lookup_int64(DDI_DEV_T_ANY, did_dinode(pd), dpnm, &vals);
if (ret != 1) {
return (0);
}
if (topo_prop_set_uint64(tn, tpgrp, tpnm, 0, (uint64_t)*vals, &ret) !=
0) {
return (topo_mod_seterrno(did_mod(pd), ret));
}
return (0);
}
static int
maybe_pcie_supported_speed(tnode_t *tn, did_t *pd, const char *dpnm,
const char *tpgrp, const char *tpnm)
{
int ret;
uint_t count;
int64_t *vals;
ret = di_prop_lookup_int64(DDI_DEV_T_ANY, did_dinode(pd), dpnm, &vals);
if (ret < 1) {
return (0);
}
count = (uint_t)ret;
if (topo_prop_set_uint64_array(tn, tpgrp, tpnm, 0, (uint64_t *)vals,
count, &ret) != 0) {
return (topo_mod_seterrno(did_mod(pd), ret));
}
return (0);
}
static int
maybe_pcie_target_speed(tnode_t *tn, did_t *pd, const char *dpnm,
const char *tpgrp, const char *tpnm)
{
di_prop_t prop = DI_PROP_NIL;
boolean_t admin = B_FALSE;
int64_t *val = NULL;
int ret;
while ((prop = di_prop_next(did_dinode(pd), prop)) != DI_PROP_NIL) {
const char *n = di_prop_name(prop);
if (strcmp(DI_PCIE_ADMIN_TAG, n) == 0) {
admin = B_TRUE;
} else if (strcmp(DI_PCIE_TARG_SPEED, n) == 0) {
if (di_prop_int64(prop, &val) != 1) {
val = NULL;
}
}
}
if (!admin || val == NULL) {
return (0);
}
if (topo_prop_set_uint64(tn, tpgrp, tpnm, 0, (uint64_t)*val, &ret) !=
0) {
return (topo_mod_seterrno(did_mod(pd), ret));
}
return (0);
}