#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <strings.h>
#include <sys/fcntl.h>
#include <fm/topo_mod.h>
#include <fm/topo_hc.h>
#include <sys/systeminfo.h>
#include <sys/smbios.h>
#include <sys/smbios_impl.h>
#include <sys/fm/protocol.h>
#include <x86pi_impl.h>
static int x86pi_enum_start(topo_mod_t *, x86pi_enum_t *);
static int x86pi_enum_gentopo(topo_mod_t *, tnode_t *);
static topo_enum_f x86pi_enum;
static tnode_t *motherchassis_node = NULL;
static topo_modops_t x86pi_ops =
{ x86pi_enum, NULL };
static topo_modinfo_t x86pi_modinfo =
{ X86PI_DESC, X86PI_SCHEME, X86PI_VERSION, &x86pi_ops };
int x86pi_smbios = 0;
int fac_done;
smbs_cnt_t stypes[SMB_TYPE_OEM_HI];
int
_topo_init(topo_mod_t *mod, topo_version_t version)
{
int result;
char isa[MAXNAMELEN];
if (getenv("TOPOX86PIDBG") != NULL) {
topo_mod_setdebug(mod);
}
topo_mod_dprintf(mod, "module initializing.\n");
if (version != TOPO_VERSION) {
(void) topo_mod_seterrno(mod, EMOD_VER_NEW);
topo_mod_dprintf(mod, "incompatible topo version %d\n",
version);
return (-1);
}
(void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
if (strncmp(isa, "i86pc", MAXNAMELEN) != 0) {
topo_mod_dprintf(mod, "not i86pc architecture: %s\n", isa);
return (-1);
}
result = topo_mod_register(mod, &x86pi_modinfo, TOPO_VERSION);
if (result < 0) {
topo_mod_dprintf(mod, "registration failed: %s\n",
topo_mod_errmsg(mod));
return (-1);
}
topo_mod_dprintf(mod, "module ready.\n");
return (0);
}
void
_topo_fini(topo_mod_t *mod)
{
topo_mod_dprintf(mod, "module finishing.\n");
topo_mod_unregister(mod);
}
static int
x86pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name,
topo_instance_t min, topo_instance_t max, void *pi_private, void *data)
{
int result;
hrtime_t starttime;
x86pi_enum_t x86pi;
starttime = gethrtime();
topo_mod_dprintf(mod, "enumeration starting.\n");
bzero(&x86pi, sizeof (x86pi_enum_t));
x86pi.t_parent = t_parent;
result = x86pi_enum_start(mod, &x86pi);
if (result != 0) {
topo_mod_dprintf(mod, "Enumeration failed.\n");
return (-1);
}
topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n",
((gethrtime() - starttime)/MICROSEC));
return (result);
}
static int
x86pi_enum_start(topo_mod_t *mod, x86pi_enum_t *x86pi)
{
int rv;
int complvl = 0;
smbios_hdl_t *shp;
char *f = "x86pi_enum_start";
shp = x86pi_smb_open(mod);
if (shp == NULL) {
topo_mod_dprintf(mod, "%s: failed to open SMBIOS\n", f);
complvl = X86PI_NONE;
} else {
complvl = x86pi_check_comp(mod);
}
topo_mod_dprintf(mod, "%s: SMBIOS x86pi compliance: %s\n", f,
complvl == X86PI_FULL ? "FULL" : "NONE");
if (complvl == X86PI_NONE) {
topo_mod_dprintf(mod,
"%s: Calling legacy enumeration\n", f);
return (topo_mod_enummap(mod, x86pi->t_parent,
"i86pc-legacy", FM_FMRI_SCHEME_HC));
}
x86pi->priv = (void *)shp;
x86pi_smbios = complvl;
if (x86pi_hbr_enum_init(mod) < 0) {
topo_mod_dprintf(mod, "%s: x86pi_hbr_enum_init() failed.\n", f);
return (-1);
}
fac_done = 0;
rv = x86pi_enum_gentopo(mod, x86pi->t_parent);
x86pi_hbr_enum_fini(mod);
if (rv != 0) {
return (-1);
}
x86pi->mod = mod;
if (fac_done == 0) {
(void) topo_mod_enummap(mod, motherchassis_node, "chassis",
FM_FMRI_SCHEME_HC);
(void) topo_mod_enummap(mod, motherchassis_node, "fan",
FM_FMRI_SCHEME_HC);
(void) topo_mod_enummap(mod, motherchassis_node, "psu",
FM_FMRI_SCHEME_HC);
}
topo_mod_dprintf(mod, "%s: done.\n", f);
return (rv);
}
static int
x86pi_enum_gentopo(topo_mod_t *mod, tnode_t *t_parent)
{
int rv;
int nch, nbb, ncmp, i;
int ch_smbid, bb_smbid;
tnode_t *chassis_node = NULL;
tnode_t *basebd_node = NULL;
smbs_cnt_t *smbc;
tnode_t *pnode = NULL;
id_t psmbid;
int notvisited;
int bb_count, ch_count;
int min, max;
int ch_inst = 0;
int disk_inst = 0;
topo_instance_t hbri = 0, rci = 0;
smbios_pciexrc_t hbr;
smbios_port_ext_t export;
char *f = "x86pi_enum_gentopo";
smbios_hdl_t *shp;
shp = topo_mod_smbios(mod);
if (shp == NULL) {
topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
return (-1);
}
if (t_parent == NULL) {
topo_mod_dprintf(mod, "%s: NULL parent\n", f);
return (-1);
}
stypes[SMB_TYPE_CHASSIS].type = SMB_TYPE_CHASSIS;
x86pi_smb_strcnt(mod, &stypes[SMB_TYPE_CHASSIS]);
ch_count = stypes[SMB_TYPE_CHASSIS].count;
for (nch = 0; nch < ch_count; nch++) {
topo_mod_dprintf(mod, "%s: found %d chassis\n", f,
stypes[SMB_TYPE_CHASSIS].count);
ch_smbid = stypes[SMB_TYPE_CHASSIS].ids[nch].id;
if (nch == 0)
motherchassis_node = chassis_node =
x86pi_gen_chassis(mod, t_parent, ch_smbid,
ch_inst++);
else {
if (motherchassis_node != NULL)
chassis_node = x86pi_gen_chassis(mod,
motherchassis_node, ch_smbid, ch_inst++);
else
chassis_node = x86pi_gen_chassis(mod,
t_parent, ch_smbid, ch_inst++);
}
if (chassis_node == NULL) {
topo_mod_dprintf(mod,
"%s: Failed to create chassis %d\n", f, nch);
continue;
}
stypes[SMB_TYPE_CHASSIS].ids[nch].node = chassis_node;
smbc = &stypes[SUN_OEM_EXT_PORT];
smbc->type = SUN_OEM_EXT_PORT;
x86pi_smb_strcnt(mod, smbc);
if (smbc->count > 0) {
rv = topo_node_range_create(mod, chassis_node, BAY, 0,
smbc->count + 1);
if (rv != 0) {
topo_mod_dprintf(mod,
"%s: Failed to create %s range: %s\n",
f, BAY, topo_mod_errmsg(mod));
continue;
}
} else {
topo_mod_dprintf(mod,
"Skipping disk bay enumeration\n");
}
for (i = 0; i < smbc->count; i++) {
if (smbios_info_extport(shp, smbc->ids[i].id,
&export) != 0) {
topo_mod_dprintf(mod,
"smbios_info_export failed: id = %d\n",
(int)smbc->ids[i].id);
continue;
}
if (export.smbporte_chassis != ch_smbid)
continue;
rv = x86pi_gen_bay(mod, chassis_node, &export,
disk_inst);
if (rv != 0)
topo_mod_dprintf(mod,
"Failed to create disk %d\n", i);
disk_inst++;
}
}
stypes[SMB_TYPE_BASEBOARD].type = SMB_TYPE_BASEBOARD;
x86pi_smb_strcnt(mod, &stypes[SMB_TYPE_BASEBOARD]);
bb_count = notvisited = stypes[SMB_TYPE_BASEBOARD].count;
for (nbb = 0; nbb < bb_count; nbb++) {
stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 0;
stypes[SMB_TYPE_BASEBOARD].ids[nbb].con_by_id = 0;
stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = NULL;
}
(void) x86pi_bb_contains(mod);
min = 0;
nbb = 0;
do {
if (nbb > bb_count && notvisited)
nbb = 0;
else if (nbb > bb_count && !notvisited)
break;
if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited ==
X86PI_VISITED) {
nbb++;
continue;
}
bb_smbid = x86pi_bb_topparent(mod, nbb, &pnode, &psmbid);
if (bb_smbid == -1 || pnode == NULL) {
topo_mod_dprintf(mod,
"Failed to get BaseBoard node (%d): parent\n",
nbb);
return (-1);
}
if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].id != bb_smbid) {
for (int i = 0; i < bb_count; i++) {
if (bb_smbid ==
stypes[SMB_TYPE_BASEBOARD].ids[i].id) {
stypes[SMB_TYPE_BASEBOARD].ids[i].\
visited = 1;
notvisited--;
break;
}
}
} else {
stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 1;
notvisited--;
}
basebd_node = x86pi_gen_bboard(mod, pnode, bb_smbid,
nbb, psmbid);
if (basebd_node == NULL) {
topo_mod_dprintf(mod,
"Failed to create BaseBoard node (%d)\n", nbb);
nbb++;
continue;
}
stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = basebd_node;
ncmp = x86pi_bb_getchips(mod, nbb, bb_count);
if (ncmp > 0) {
max = min + ncmp - 1;
topo_mod_dprintf(mod, "%s: loading chip enum\n", f);
if (topo_mod_load(mod, CHIP, TOPO_VERSION) == NULL) {
topo_mod_dprintf(mod,
"%s: Failed to load %s module: %s\n", f,
CHIP, topo_strerror(topo_mod_errno(mod)));
} else {
topo_mod_dprintf(mod,
"%s: chip range %d to %d\n",
f, min, max);
rv = topo_node_range_create(mod, basebd_node,
CHIP, min, max);
if (rv != 0) {
topo_mod_dprintf(mod,
"%s: Failed to create node range: "
"%s\n", f,
topo_strerror(topo_mod_errno(mod)));
} else {
topo_mod_dprintf(mod, "%s: calling"
" chip enum\n", f);
rv =
topo_mod_enumerate(mod, basebd_node,
CHIP, CHIP, min, max,
&x86pi_smbios);
min = max + 1;
if (rv != 0)
topo_mod_dprintf(mod, "%s:%s"
"enumeration failed: \n",
f, CHIP);
}
}
}
rv = topo_node_range_create(mod, basebd_node, HOSTBRIDGE,
0, 255);
if (rv != 0) {
topo_mod_dprintf(mod,
"%s: Failed to create %s range: %s\n",
f, HOSTBRIDGE, topo_mod_errmsg(mod));
continue;
}
smbc = &stypes[SUN_OEM_PCIEXRC];
smbc->type = SUN_OEM_PCIEXRC;
x86pi_smb_strcnt(mod, smbc);
for (i = 0; i < smbc->count; i++) {
if (smbios_info_pciexrc(shp, smbc->ids[i].id,
&hbr) != 0) {
topo_mod_dprintf(mod,
"smbios_info_pciexrc failed: "
"id = %d\n", (int)smbc->ids[i].id);
continue;
}
if (hbr.smbpcie_bb != bb_smbid)
continue;
rv = x86pi_gen_hbr(mod, basebd_node,
smbc->ids[i].id, hbri, &rci);
if (rv != 0) {
topo_mod_dprintf(mod, "couldn't create "
"hostbridge=%" PRIu64 "\n", hbri);
}
hbri++;
}
nbb++;
} while (notvisited);
return (0);
}