#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/smbios.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/topo_method.h>
#include "topo_pcie_impl.h"
typedef struct smbios_dev_entry {
const char *sde_label;
uint16_t sde_sg;
uint8_t sde_bus;
uint8_t sde_df;
} smbios_dev_entry_t;
typedef struct smbios_proc_entry {
const char *spe_label;
} smbios_proc_entry_t;
typedef struct mod_pcie_privdata {
smbios_proc_entry_t *mpp_procs;
uint_t mpp_nprocs;
uint_t mpp_nprocs_alloc;
smbios_dev_entry_t *mpp_slots;
uint_t mpp_nslots;
uint_t mpp_nslots_alloc;
smbios_dev_entry_t *mpp_obdevs;
uint_t mpp_nobdevs;
uint_t mpp_nobdevs_alloc;
} mod_pcie_privdata_t;
static const char *
smbios_find_slot_by_bus(const mod_pcie_privdata_t *pd, uint8_t bus)
{
for (uint_t i = 0; i < pd->mpp_nslots; i++) {
if (pd->mpp_slots[i].sde_bus == bus)
return (pd->mpp_slots[i].sde_label);
}
return (NULL);
}
static const char *
smbios_find_obdev_by_bus(const mod_pcie_privdata_t *pd, uint8_t bus)
{
for (uint_t i = 0; i < pd->mpp_nobdevs; i++) {
if (pd->mpp_obdevs[i].sde_bus == bus)
return (pd->mpp_obdevs[i].sde_label);
}
return (NULL);
}
static tnode_t *
decorate_port(const mod_pcie_privdata_t *pd, topo_mod_t *mod,
const pcie_node_t *node __unused, tnode_t *tn)
{
tnode_t *ptn;
const char *label;
uint32_t *busrange = NULL;
uint_t nelem = 0;
int err;
uint_t secbus;
ptn = topo_node_parent(tn);
if (ptn == NULL || strcmp(topo_node_name(ptn), "link") == 0)
return (tn);
if (topo_prop_get_uint32_array(ptn, TOPO_PCIE_PGROUP_PCI_CFG,
TOPO_PCIE_PCI_BUS_RANGE, &busrange, &nelem, &err) != 0 ||
nelem < 1) {
topo_mod_free(mod, busrange, nelem * sizeof (uint32_t));
return (tn);
}
secbus = (uint8_t)busrange[0];
topo_mod_free(mod, busrange, nelem * sizeof (uint32_t));
topo_mod_dprintf(mod,
"decorate port: parent %s%" PRIu64 " secondary bus 0x%02x",
topo_node_name(ptn), topo_node_instance(ptn), secbus);
label = smbios_find_slot_by_bus(pd, secbus);
if (label == NULL)
label = smbios_find_obdev_by_bus(pd, secbus);
if (label != NULL) {
topo_mod_dprintf(mod, "decorate port: label '%s'", label);
(void) topo_node_label_set(tn, label, &err);
}
return (tn);
}
static tnode_t *
decorate_cpu(const mod_pcie_privdata_t *pd, topo_mod_t *mod,
const pcie_node_t *node __unused, tnode_t *tn)
{
topo_instance_t inst = topo_node_instance(tn);
const char *label;
int err;
if (inst >= pd->mpp_nprocs)
return (tn);
label = pd->mpp_procs[inst].spe_label;
if (label != NULL && label[0] != '\0') {
topo_mod_dprintf(mod,
"decorate cpu%" PRIu64 ": label '%s'", inst, label);
(void) topo_node_label_set(tn, label, &err);
}
return (tn);
}
tnode_t *
mod_pcie_platform_topo_node_decorate(topo_mod_t *mod, const pcie_t *pcie,
const pcie_node_t *node, tnode_t *tn)
{
const mod_pcie_privdata_t *pd;
const char *name;
pd = pcie_get_platdata(pcie);
if (pd == NULL) {
topo_mod_dprintf(mod, "decorate: no privdata");
return (tn);
}
name = topo_node_name(tn);
topo_mod_dprintf(mod, "decorate: %s%" PRIu64,
name, topo_node_instance(tn));
if (strcmp(name, CPU) == 0)
return (decorate_cpu(pd, mod, node, tn));
else if (strcmp(name, "port") == 0)
return (decorate_port(pd, mod, node, tn));
return (tn);
}
nvlist_t *
mod_pcie_platform_auth(topo_mod_t *mod, const pcie_t *pcie, tnode_t *parent)
{
return (topo_mod_auth(mod, parent));
}
typedef struct mod_pcie_smbios_collect {
topo_mod_t *sc_mod;
smbios_hdl_t *sc_shp;
smbios_dev_entry_t *sc_slots;
uint_t sc_nslots;
smbios_dev_entry_t *sc_obdevs;
uint_t sc_nobdevs;
smbios_proc_entry_t *sc_procs;
uint_t sc_nprocs;
} mod_pcie_smbios_collect_t;
static int
smbios_collect_count_cb(smbios_hdl_t *shp __unused,
const smbios_struct_t *sp, void *arg)
{
mod_pcie_smbios_collect_t *sc = arg;
switch (sp->smbstr_type) {
case SMB_TYPE_PROCESSOR:
sc->sc_nprocs++;
break;
case SMB_TYPE_OBDEVEXT:
sc->sc_nobdevs++;
break;
case SMB_TYPE_SLOT:
sc->sc_nslots++;
break;
}
return (0);
}
static int
smbios_collect_cb(smbios_hdl_t *shp,
const smbios_struct_t *sp, void *arg)
{
mod_pcie_smbios_collect_t *sc = arg;
switch (sp->smbstr_type) {
case SMB_TYPE_PROCESSOR: {
smbios_proc_entry_t *entry;
smbios_processor_t proc;
smbios_info_t info;
entry = &sc->sc_procs[sc->sc_nprocs++];
if (smbios_info_processor(shp, sp->smbstr_id, &proc) != 0)
break;
if (!SMB_PRSTATUS_PRESENT(proc.smbp_status))
break;
if (smbios_info_common(shp, sp->smbstr_id, &info) != 0)
break;
entry->spe_label = info.smbi_location;
topo_mod_dprintf(sc->sc_mod, "SMBIOS processor[%u]: '%s'",
sc->sc_nprocs - 1, info.smbi_location);
break;
}
case SMB_TYPE_OBDEVEXT: {
smbios_dev_entry_t *entry;
smbios_obdev_ext_t ob;
if (smbios_info_obdevs_ext(shp, sp->smbstr_id, &ob) != 0)
break;
entry = &sc->sc_obdevs[sc->sc_nobdevs++];
entry->sde_label = ob.smboe_name;
entry->sde_sg = ob.smboe_sg;
entry->sde_bus = ob.smboe_bus;
entry->sde_df = ob.smboe_df;
topo_mod_dprintf(sc->sc_mod,
"SMBIOS obdev: '%s' seg %u bus 0x%02x df 0x%02x",
ob.smboe_name, ob.smboe_sg, ob.smboe_bus, ob.smboe_df);
break;
}
case SMB_TYPE_SLOT: {
smbios_dev_entry_t *entry;
smbios_slot_t slot;
if (smbios_info_slot(shp, sp->smbstr_id, &slot) != 0)
break;
if (slot.smbl_bus == 0xff)
break;
entry = &sc->sc_slots[sc->sc_nslots++];
entry->sde_label = slot.smbl_name;
entry->sde_sg = slot.smbl_sg;
entry->sde_bus = slot.smbl_bus;
entry->sde_df = slot.smbl_df;
topo_mod_dprintf(sc->sc_mod,
"SMBIOS slot: '%s' seg %u bus 0x%02x df 0x%02x",
slot.smbl_name, slot.smbl_sg, slot.smbl_bus, slot.smbl_df);
break;
}
default:
break;
}
return (0);
}
static bool
smbios_collect_init(topo_mod_t *mod, mod_pcie_privdata_t *pd)
{
mod_pcie_smbios_collect_t sc = { 0 };
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp == NULL) {
topo_mod_dprintf(mod, "SMBIOS not available");
return (true);
}
sc.sc_mod = mod;
sc.sc_shp = shp;
VERIFY0(smbios_iter(shp, smbios_collect_count_cb, &sc));
topo_mod_dprintf(mod,
"SMBIOS counts: processors: %u on-board devices: %u slots: %u",
sc.sc_nprocs, sc.sc_nobdevs, sc.sc_nslots);
if (sc.sc_nprocs > 0) {
pd->mpp_procs = topo_mod_zalloc(mod,
sc.sc_nprocs * sizeof (smbios_proc_entry_t));
if (pd->mpp_procs == NULL)
return (false);
pd->mpp_nprocs_alloc = sc.sc_nprocs;
}
if (sc.sc_nobdevs > 0) {
pd->mpp_obdevs = topo_mod_zalloc(mod,
sc.sc_nobdevs * sizeof (smbios_dev_entry_t));
if (pd->mpp_obdevs == NULL)
return (false);
pd->mpp_nobdevs_alloc = sc.sc_nobdevs;
}
if (sc.sc_nslots > 0) {
pd->mpp_slots = topo_mod_zalloc(mod,
sc.sc_nslots * sizeof (smbios_dev_entry_t));
if (pd->mpp_slots == NULL)
return (false);
pd->mpp_nslots_alloc = sc.sc_nslots;
}
sc.sc_procs = pd->mpp_procs;
sc.sc_obdevs = pd->mpp_obdevs;
sc.sc_slots = pd->mpp_slots;
sc.sc_nprocs = sc.sc_nobdevs = sc.sc_nslots = 0;
VERIFY0(smbios_iter(shp, smbios_collect_cb, &sc));
topo_mod_dprintf(mod,
"SMBIOS populated: processors: %u on-board devices: %u slots: %u",
sc.sc_nprocs, sc.sc_nobdevs, sc.sc_nslots);
pd->mpp_nprocs = sc.sc_nprocs;
pd->mpp_nobdevs = sc.sc_nobdevs;
pd->mpp_nslots = sc.sc_nslots;
return (true);
}
static void
privdata_free(topo_mod_t *mod, mod_pcie_privdata_t *pd)
{
if (pd->mpp_procs != NULL) {
topo_mod_free(mod, pd->mpp_procs,
pd->mpp_nprocs_alloc * sizeof (smbios_proc_entry_t));
}
if (pd->mpp_obdevs != NULL) {
topo_mod_free(mod, pd->mpp_obdevs,
pd->mpp_nobdevs_alloc * sizeof (smbios_dev_entry_t));
}
if (pd->mpp_slots != NULL) {
topo_mod_free(mod, pd->mpp_slots,
pd->mpp_nslots_alloc * sizeof (smbios_dev_entry_t));
}
topo_mod_free(mod, pd, sizeof (*pd));
}
bool
mod_pcie_platform_init(topo_mod_t *mod, pcie_t *pcie)
{
mod_pcie_privdata_t *pd;
topo_mod_dprintf(mod, "%s start", __func__);
if ((pd = topo_mod_zalloc(mod, sizeof (*pd))) == NULL)
return (false);
if (!smbios_collect_init(mod, pd)) {
topo_mod_dprintf(mod, "SMBIOS collection failed");
privdata_free(mod, pd);
return (false);
}
return (pcie_set_platdata(pcie, pd));
}
void
mod_pcie_platform_fini(topo_mod_t *mod, pcie_t *pcie)
{
mod_pcie_privdata_t *pd;
if ((pd = pcie_get_platdata(pcie)) == NULL)
return;
privdata_free(mod, pd);
(void) pcie_set_platdata(pcie, NULL);
}