#include "imc.h"
#ifndef _KERNEL
#include <stdint.h>
#include <strings.h>
#endif
static nvlist_t *
imc_dump_sad(imc_sad_t *sad)
{
uint_t i;
nvlist_t *nvl;
nvlist_t *rules[IMC_MAX_SAD_RULES];
nvlist_t *routes[IMC_MAX_SAD_MCROUTES];
nvl = fnvlist_alloc();
fnvlist_add_uint32(nvl, "isad_flags", sad->isad_flags);
fnvlist_add_uint32(nvl, "isad_valid", sad->isad_valid);
fnvlist_add_uint64(nvl, "isad_tolm", sad->isad_tolm);
fnvlist_add_uint64(nvl, "isad_tohm", sad->isad_tohm);
for (i = 0; i < sad->isad_nrules; i++) {
nvlist_t *n = fnvlist_alloc();
imc_sad_rule_t *r = &sad->isad_rules[i];
fnvlist_add_boolean_value(n, "isr_enable", r->isr_enable);
fnvlist_add_boolean_value(n, "isr_a7mode", r->isr_a7mode);
fnvlist_add_boolean_value(n, "isr_need_mod3", r->isr_need_mod3);
fnvlist_add_uint64(n, "isr_limit", r->isr_limit);
fnvlist_add_uint32(n, "isr_type", r->isr_type);
fnvlist_add_uint32(n, "isr_imode", r->isr_imode);
fnvlist_add_uint32(n, "isr_mod_mode", r->isr_mod_mode);
fnvlist_add_uint32(n, "isr_mod_type", r->isr_mod_type);
fnvlist_add_uint8_array(n, "isr_targets", r->isr_targets,
r->isr_ntargets);
rules[i] = n;
}
fnvlist_add_nvlist_array(nvl, "isad_rules", rules, sad->isad_nrules);
for (i = 0; i < sad->isad_nrules; i++) {
nvlist_free(rules[i]);
}
if (sad->isad_mcroute.ismc_nroutes == 0) {
return (nvl);
}
for (i = 0; i < sad->isad_mcroute.ismc_nroutes; i++) {
nvlist_t *r = fnvlist_alloc();
imc_sad_mcroute_entry_t *e =
&sad->isad_mcroute.ismc_mcroutes[i];
fnvlist_add_uint8(r, "ismce_imc", e->ismce_imc);
fnvlist_add_uint8(r, "ismce_pchannel", e->ismce_pchannel);
routes[i] = r;
}
fnvlist_add_nvlist_array(nvl, "isad_mcroute", routes, i);
for (i = 0; i < sad->isad_mcroute.ismc_nroutes; i++) {
nvlist_free(routes[i]);
}
return (nvl);
}
static nvlist_t *
imc_dump_tad(imc_tad_t *tad)
{
uint_t i;
nvlist_t *nvl;
nvlist_t *rules[IMC_MAX_TAD_RULES];
nvl = fnvlist_alloc();
fnvlist_add_uint32(nvl, "itad_valid", tad->itad_valid);
fnvlist_add_uint32(nvl, "itad_flags", tad->itad_flags);
for (i = 0; i < tad->itad_nrules; i++) {
nvlist_t *t = fnvlist_alloc();
imc_tad_rule_t *r = &tad->itad_rules[i];
fnvlist_add_uint64(t, "itr_base", r->itr_base);
fnvlist_add_uint64(t, "itr_limit", r->itr_limit);
fnvlist_add_uint8(t, "itr_sock_way", r->itr_sock_way);
fnvlist_add_uint8(t, "itr_chan_way", r->itr_chan_way);
fnvlist_add_uint32(t, "itr_sock_gran", r->itr_sock_gran);
fnvlist_add_uint32(t, "itr_chan_gran", r->itr_chan_gran);
fnvlist_add_uint8_array(t, "itr_targets", r->itr_targets,
r->itr_ntargets);
rules[i] = t;
}
fnvlist_add_nvlist_array(nvl, "itad_rules", rules, tad->itad_nrules);
for (i = 0; i < tad->itad_nrules; i++) {
nvlist_free(rules[i]);
}
return (nvl);
}
static nvlist_t *
imc_dump_channel(imc_channel_t *chan)
{
uint_t i;
nvlist_t *nvl;
nvlist_t *dimms[IMC_MAX_DIMMPERCHAN];
nvlist_t *ranks[IMC_MAX_RANK_WAYS];
nvl = fnvlist_alloc();
fnvlist_add_uint32(nvl, "ich_valid", chan->ich_valid);
for (i = 0; i < chan->ich_ndimms; i++) {
nvlist_t *d = fnvlist_alloc();
imc_dimm_t *dimm = &chan->ich_dimms[i];
fnvlist_add_uint32(d, "idimm_valid", dimm->idimm_valid);
fnvlist_add_boolean_value(d, "idimm_present",
dimm->idimm_present);
if (!dimm->idimm_present)
goto add;
fnvlist_add_uint8(d, "idimm_nbanks", dimm->idimm_nbanks);
fnvlist_add_uint8(d, "idimm_nranks", dimm->idimm_nranks);
fnvlist_add_uint8(d, "idimm_width", dimm->idimm_width);
fnvlist_add_uint8(d, "idimm_density", dimm->idimm_density);
fnvlist_add_uint8(d, "idimm_nrows", dimm->idimm_nrows);
fnvlist_add_uint8(d, "idimm_ncolumns", dimm->idimm_ncolumns);
fnvlist_add_uint64(d, "idimm_size", dimm->idimm_size);
add:
dimms[i] = d;
}
fnvlist_add_nvlist_array(nvl, "ich_dimms", dimms, i);
for (i = 0; i < chan->ich_ndimms; i++) {
nvlist_free(dimms[i]);
}
fnvlist_add_uint64_array(nvl, "ich_tad_offsets", chan->ich_tad_offsets,
chan->ich_ntad_offsets);
for (i = 0; i < chan->ich_nrankileaves; i++) {
uint_t j;
nvlist_t *r = fnvlist_alloc();
nvlist_t *ileaves[IMC_MAX_RANK_INTERLEAVES];
imc_rank_ileave_t *rank = &chan->ich_rankileaves[i];
fnvlist_add_boolean_value(r, "irle_enabled",
rank->irle_enabled);
fnvlist_add_uint8(r, "irle_nways", rank->irle_nways);
fnvlist_add_uint8(r, "irle_nwaysbits", rank->irle_nwaysbits);
fnvlist_add_uint64(r, "irle_limit", rank->irle_limit);
for (j = 0; j < rank->irle_nentries; j++) {
nvlist_t *e = fnvlist_alloc();
fnvlist_add_uint8(e, "irle_target",
rank->irle_entries[j].irle_target);
fnvlist_add_uint64(e, "irle_offset",
rank->irle_entries[j].irle_offset);
ileaves[j] = e;
}
fnvlist_add_nvlist_array(r, "irle_entries", ileaves, j);
for (j = 0; j < rank->irle_nentries; j++) {
nvlist_free(ileaves[j]);
}
ranks[i] = r;
}
fnvlist_add_nvlist_array(nvl, "ich_rankileaves", ranks, i);
for (i = 0; i < chan->ich_nrankileaves; i++) {
nvlist_free(ranks[i]);
}
return (nvl);
}
static nvlist_t *
imc_dump_mc(imc_mc_t *mc)
{
uint_t i;
nvlist_t *nvl;
nvlist_t *channels[IMC_MAX_CHANPERMC];
nvl = fnvlist_alloc();
fnvlist_add_boolean_value(nvl, "icn_ecc", mc->icn_ecc);
fnvlist_add_boolean_value(nvl, "icn_lockstep", mc->icn_lockstep);
fnvlist_add_boolean_value(nvl, "icn_closed", mc->icn_closed);
fnvlist_add_uint32(nvl, "icn_dimm_type", mc->icn_dimm_type);
for (i = 0; i < mc->icn_nchannels; i++) {
channels[i] = imc_dump_channel(&mc->icn_channels[i]);
}
fnvlist_add_nvlist_array(nvl, "icn_channels", channels, i);
for (i = 0; i < mc->icn_nchannels; i++) {
nvlist_free(channels[i]);
}
return (nvl);
}
static nvlist_t *
imc_dump_socket(imc_socket_t *sock)
{
uint_t i;
nvlist_t *nvl, *sad;
nvlist_t *tad[IMC_MAX_TAD];
nvlist_t *mc[IMC_MAX_IMCPERSOCK];
nvl = fnvlist_alloc();
sad = imc_dump_sad(&sock->isock_sad);
fnvlist_add_nvlist(nvl, "isock_sad", sad);
nvlist_free(sad);
for (i = 0; i < sock->isock_ntad; i++) {
tad[i] = imc_dump_tad(&sock->isock_tad[i]);
}
fnvlist_add_nvlist_array(nvl, "isock_tad", tad, i);
for (i = 0; i < sock->isock_ntad; i++) {
fnvlist_free(tad[i]);
}
fnvlist_add_uint32(nvl, "isock_nodeid", sock->isock_nodeid);
for (i = 0; i < sock->isock_nimc; i++) {
mc[i] = imc_dump_mc(&sock->isock_imcs[i]);
}
fnvlist_add_nvlist_array(nvl, "isock_imcs", mc, i);
for (i = 0; i < sock->isock_nimc; i++) {
fnvlist_free(mc[i]);
}
return (nvl);
}
nvlist_t *
imc_dump_decoder(imc_t *imc)
{
uint_t i;
nvlist_t *nvl, *invl;
nvlist_t *sockets[IMC_MAX_SOCKETS];
nvl = fnvlist_alloc();
fnvlist_add_uint32(nvl, "mc_dump_version", 0);
fnvlist_add_string(nvl, "mc_dump_driver", "imc");
invl = fnvlist_alloc();
fnvlist_add_uint32(invl, "imc_gen", imc->imc_gen);
for (i = 0; i < imc->imc_nsockets; i++) {
sockets[i] = imc_dump_socket(&imc->imc_sockets[i]);
}
fnvlist_add_nvlist_array(invl, "imc_sockets", sockets, i);
fnvlist_add_nvlist(nvl, "imc", invl);
for (i = 0; i < imc->imc_nsockets; i++) {
nvlist_free(sockets[i]);
}
nvlist_free(invl);
return (nvl);
}
static boolean_t
imc_restore_sad(nvlist_t *nvl, imc_sad_t *sad)
{
nvlist_t **rules, **routes;
uint_t i, nroutes;
if (nvlist_lookup_uint32(nvl, "isad_flags", &sad->isad_flags) != 0 ||
nvlist_lookup_uint32(nvl, "isad_valid", &sad->isad_valid) != 0 ||
nvlist_lookup_uint64(nvl, "isad_tolm", &sad->isad_tolm) != 0 ||
nvlist_lookup_uint64(nvl, "isad_tohm", &sad->isad_tohm) != 0 ||
nvlist_lookup_nvlist_array(nvl, "isad_rules",
&rules, &sad->isad_nrules) != 0) {
return (B_FALSE);
}
for (i = 0; i < sad->isad_nrules; i++) {
imc_sad_rule_t *r = &sad->isad_rules[i];
uint8_t *targs;
if (nvlist_lookup_boolean_value(rules[i], "isr_enable",
&r->isr_enable) != 0 ||
nvlist_lookup_boolean_value(rules[i], "isr_a7mode",
&r->isr_a7mode) != 0 ||
nvlist_lookup_boolean_value(rules[i], "isr_need_mod3",
&r->isr_need_mod3) != 0 ||
nvlist_lookup_uint64(rules[i], "isr_limit",
&r->isr_limit) != 0 ||
nvlist_lookup_uint32(rules[i], "isr_type",
&r->isr_type) != 0 ||
nvlist_lookup_uint32(rules[i], "isr_imode",
&r->isr_imode) != 0 ||
nvlist_lookup_uint32(rules[i], "isr_mod_mode",
&r->isr_mod_mode) != 0 ||
nvlist_lookup_uint32(rules[i], "isr_mod_type",
&r->isr_mod_type) != 0 ||
nvlist_lookup_uint8_array(rules[i], "isr_targets", &targs,
&r->isr_ntargets) != 0 ||
r->isr_ntargets > IMC_MAX_SAD_RULES) {
return (B_FALSE);
}
bcopy(targs, r->isr_targets, r->isr_ntargets *
sizeof (uint8_t));
}
if (nvlist_lookup_nvlist_array(nvl, "isad_mcroute", &routes,
&nroutes) == 0) {
if (nroutes > IMC_MAX_SAD_MCROUTES)
return (B_FALSE);
sad->isad_mcroute.ismc_nroutes = nroutes;
for (i = 0; i < nroutes; i++) {
imc_sad_mcroute_entry_t *r =
&sad->isad_mcroute.ismc_mcroutes[i];
if (nvlist_lookup_uint8(routes[i], "ismce_imc",
&r->ismce_imc) != 0 ||
nvlist_lookup_uint8(routes[i], "ismce_pchannel",
&r->ismce_pchannel) != 0) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
static boolean_t
imc_restore_tad(nvlist_t *nvl, imc_tad_t *tad)
{
nvlist_t **rules;
if (nvlist_lookup_uint32(nvl, "itad_valid", &tad->itad_valid) != 0 ||
nvlist_lookup_uint32(nvl, "itad_flags", &tad->itad_flags) != 0 ||
nvlist_lookup_nvlist_array(nvl, "itad_rules", &rules,
&tad->itad_nrules) != 0 || tad->itad_nrules > IMC_MAX_TAD_RULES) {
return (B_FALSE);
}
for (uint_t i = 0; i < tad->itad_nrules; i++) {
imc_tad_rule_t *r = &tad->itad_rules[i];
uint8_t *targs;
if (nvlist_lookup_uint64(rules[i], "itr_base",
&r->itr_base) != 0 ||
nvlist_lookup_uint64(rules[i], "itr_limit",
&r->itr_limit) != 0 ||
nvlist_lookup_uint8(rules[i], "itr_sock_way",
&r->itr_sock_way) != 0 ||
nvlist_lookup_uint8(rules[i], "itr_chan_way",
&r->itr_chan_way) != 0 ||
nvlist_lookup_uint32(rules[i], "itr_sock_gran",
&r->itr_sock_gran) != 0 ||
nvlist_lookup_uint32(rules[i], "itr_chan_gran",
&r->itr_chan_gran) != 0 ||
nvlist_lookup_uint8_array(rules[i], "itr_targets",
&targs, &r->itr_ntargets) != 0 ||
r->itr_ntargets > IMC_MAX_TAD_TARGETS) {
return (B_FALSE);
}
bcopy(targs, r->itr_targets, r->itr_ntargets *
sizeof (uint8_t));
}
return (B_TRUE);
}
static boolean_t
imc_restore_channel(nvlist_t *nvl, imc_channel_t *chan)
{
nvlist_t **dimms, **rir;
uint64_t *tadoff;
if (nvlist_lookup_uint32(nvl, "ich_valid", &chan->ich_valid) != 0 ||
nvlist_lookup_nvlist_array(nvl, "ich_dimms", &dimms,
&chan->ich_ndimms) != 0 ||
chan->ich_ndimms > IMC_MAX_DIMMPERCHAN ||
nvlist_lookup_uint64_array(nvl, "ich_tad_offsets", &tadoff,
&chan->ich_ntad_offsets) != 0 ||
chan->ich_ntad_offsets > IMC_MAX_TAD_RULES ||
nvlist_lookup_nvlist_array(nvl, "ich_rankileaves", &rir,
&chan->ich_nrankileaves) != 0 ||
chan->ich_nrankileaves > IMC_MAX_RANK_WAYS) {
return (B_FALSE);
}
for (uint_t i = 0; i < chan->ich_ndimms; i++) {
imc_dimm_t *d = &chan->ich_dimms[i];
if (nvlist_lookup_uint32(dimms[i], "idimm_valid",
&d->idimm_valid) != 0 ||
nvlist_lookup_boolean_value(dimms[i], "idimm_present",
&d->idimm_present) != 0) {
return (B_FALSE);
}
if (!d->idimm_present)
continue;
if (nvlist_lookup_uint8(dimms[i], "idimm_nbanks",
&d->idimm_nbanks) != 0 ||
nvlist_lookup_uint8(dimms[i], "idimm_nranks",
&d->idimm_nranks) != 0 ||
nvlist_lookup_uint8(dimms[i], "idimm_width",
&d->idimm_width) != 0 ||
nvlist_lookup_uint8(dimms[i], "idimm_density",
&d->idimm_density) != 0 ||
nvlist_lookup_uint8(dimms[i], "idimm_nrows",
&d->idimm_nrows) != 0 ||
nvlist_lookup_uint8(dimms[i], "idimm_ncolumns",
&d->idimm_ncolumns) != 0 ||
nvlist_lookup_uint64(dimms[i], "idimm_size",
&d->idimm_size) != 0) {
return (B_FALSE);
}
}
bcopy(tadoff, chan->ich_tad_offsets, chan->ich_ntad_offsets *
sizeof (uint64_t));
for (uint_t i = 0; i < chan->ich_nrankileaves; i++) {
nvlist_t **ileaves;
imc_rank_ileave_t *r = &chan->ich_rankileaves[i];
if (nvlist_lookup_boolean_value(rir[i], "irle_enabled",
&r->irle_enabled) != 0 ||
nvlist_lookup_uint8(rir[i], "irle_nways",
&r->irle_nways) != 0 ||
nvlist_lookup_uint8(rir[i], "irle_nwaysbits",
&r->irle_nwaysbits) != 0 ||
nvlist_lookup_uint64(rir[i], "irle_limit",
&r->irle_limit) != 0 ||
nvlist_lookup_nvlist_array(rir[i], "irle_entries",
&ileaves, &r->irle_nentries) != 0 ||
r->irle_nentries > IMC_MAX_RANK_INTERLEAVES) {
return (B_FALSE);
}
for (uint_t j = 0; j < r->irle_nentries; j++) {
imc_rank_ileave_entry_t *ril = &r->irle_entries[j];
if (nvlist_lookup_uint8(ileaves[j], "irle_target",
&ril->irle_target) != 0 ||
nvlist_lookup_uint64(ileaves[j], "irle_offset",
&ril->irle_offset) != 0) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
static boolean_t
imc_restore_mc(nvlist_t *nvl, imc_mc_t *mc)
{
nvlist_t **channels;
if (nvlist_lookup_boolean_value(nvl, "icn_ecc", &mc->icn_ecc) != 0 ||
nvlist_lookup_boolean_value(nvl, "icn_lockstep",
&mc->icn_lockstep) != 0 ||
nvlist_lookup_boolean_value(nvl, "icn_closed",
&mc->icn_closed) != 0 ||
nvlist_lookup_uint32(nvl, "icn_dimm_type",
&mc->icn_dimm_type) != 0 ||
nvlist_lookup_nvlist_array(nvl, "icn_channels", &channels,
&mc->icn_nchannels) != 0 || mc->icn_nchannels > IMC_MAX_CHANPERMC) {
return (B_FALSE);
}
for (uint_t i = 0; i < mc->icn_nchannels; i++) {
if (!imc_restore_channel(channels[i], &mc->icn_channels[i])) {
return (B_FALSE);
}
}
return (B_TRUE);
}
static boolean_t
imc_restore_socket(nvlist_t *nvl, imc_socket_t *sock)
{
uint_t i;
nvlist_t *sad, **tads, **imcs;
if (nvlist_lookup_nvlist(nvl, "isock_sad", &sad) != 0 ||
nvlist_lookup_nvlist_array(nvl, "isock_tad", &tads,
&sock->isock_ntad) != 0 ||
nvlist_lookup_uint32(nvl, "isock_nodeid",
&sock->isock_nodeid) != 0 ||
nvlist_lookup_nvlist_array(nvl, "isock_imcs", &imcs,
&sock->isock_nimc) != 0 ||
sock->isock_ntad > IMC_MAX_TAD ||
sock->isock_nimc > IMC_MAX_IMCPERSOCK) {
return (B_FALSE);
}
if (!imc_restore_sad(sad, &sock->isock_sad)) {
return (B_FALSE);
}
for (i = 0; i < sock->isock_ntad; i++) {
if (!imc_restore_tad(tads[i], &sock->isock_tad[i])) {
return (B_FALSE);
}
}
for (i = 0; i < sock->isock_nimc; i++) {
if (!imc_restore_mc(imcs[i], &sock->isock_imcs[i])) {
return (B_FALSE);
}
}
return (B_TRUE);
}
boolean_t
imc_restore_decoder(nvlist_t *nvl, imc_t *imc)
{
uint_t i;
uint32_t vers;
nvlist_t *invl, **socks;
char *driver;
bzero(imc, sizeof (imc_t));
if (nvlist_lookup_uint32(nvl, "mc_dump_version", &vers) != 0 ||
vers != 0 ||
nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0 ||
strcmp(driver, "imc") != 0 ||
nvlist_lookup_nvlist(nvl, "imc", &invl) != 0) {
return (B_FALSE);
}
if (nvlist_lookup_uint32(invl, "imc_gen", &imc->imc_gen) != 0 ||
nvlist_lookup_nvlist_array(invl, "imc_sockets", &socks,
&imc->imc_nsockets) != 0 ||
imc->imc_nsockets > IMC_MAX_SOCKETS) {
return (B_FALSE);
}
for (i = 0; i < imc->imc_nsockets; i++) {
if (!imc_restore_socket(socks[i], &imc->imc_sockets[i]))
return (B_FALSE);
}
return (B_TRUE);
}