#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <unistd.h>
#include <sys/mman.h>
#include <libnvpair.h>
#include <sys/sysmacros.h>
#include <sys/mc.h>
#include "imc.h"
#include "zen_umc.h"
#define MCDECODE_USAGE 2
#define MCDECODE_WRITE (1024 * 32)
typedef struct mc_backend {
const char *mcb_name;
void *(*mcb_init)(nvlist_t *, const char *);
void (*mcb_decode_pa)(void *, uint64_t);
} mc_backend_t;
static const mc_backend_t *mc_cur_backend = NULL;
static void
mcdecode_usage(void)
{
(void) fprintf(stderr,
"Usage: mcdecode -d address -f infile | device\n"
" mcdecode -w outfile device\n"
"\n"
"\t-d decode physical address to the corresponding dimm\n"
"\t-f use decoder image from infile\n"
"\t-w write decoder snapshot state to the specified file\n");
exit(MCDECODE_USAGE);
}
static void *
mcb_imc_init(nvlist_t *nvl, const char *file)
{
imc_t *imc;
imc = calloc(1, sizeof (*imc));
if (imc == NULL) {
err(EXIT_FAILURE, "failed to allocate memory for imc_t");
}
if (!imc_restore_decoder(nvl, imc)) {
errx(EXIT_FAILURE, "failed to restore memory "
"controller snapshot in %s", file);
}
return (imc);
}
static void
mcb_imc_decode_pa(void *arg, uint64_t pa)
{
const imc_t *imc = arg;
imc_decode_state_t dec;
bzero(&dec, sizeof (dec));
if (!imc_decode_pa(imc, pa, &dec)) {
errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64
" -- 0x%x, 0x%" PRIx64, pa, dec.ids_fail,
dec.ids_fail_data);
}
(void) printf("Decoded physical address 0x%" PRIx64 "\n"
"\tchip:\t\t\t%u\n"
"\tmemory controller:\t%u\n"
"\tchannel:\t\t%u\n"
"\tdimm:\t\t\t%u\n"
"\trank:\t\t\t%u\n",
pa, dec.ids_nodeid, dec.ids_tadid, dec.ids_channelid,
dec.ids_dimmid, dec.ids_rankid);
}
static void *
mcb_umc_init(nvlist_t *nvl, const char *file)
{
zen_umc_t *umc;
umc = calloc(1, sizeof (*umc));
if (umc == NULL) {
err(EXIT_FAILURE, "failed to allocate memory for zen_umc_t");
}
if (!zen_umc_restore_decoder(nvl, umc)) {
errx(EXIT_FAILURE, "failed to restore memory "
"controller snapshot in %s", file);
}
return (umc);
}
static void
mcb_umc_decode_pa(void *arg, uint64_t pa)
{
zen_umc_t *umc = arg;
zen_umc_decoder_t dec;
uint32_t sock, die, comp;
bzero(&dec, sizeof (dec));
if (!zen_umc_decode_pa(umc, pa, &dec)) {
errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64
" -- 0x%x, 0x%" PRIx64, pa, dec.dec_fail,
dec.dec_fail_data);
}
zen_fabric_id_decompose(&umc->umc_decomp, dec.dec_targ_fabid, &sock,
&die, &comp);
(void) printf("Decoded physical address 0x%" PRIx64 "\n"
"\tsocket:\t\t\t%u\n"
"\tdie:\t\t\t%u\n"
"\tchannel:\t\t%u\n"
"\tchannel address\t\t0x%" PRIx64 "\n"
"\tdimm:\t\t\t%u\n"
"\trow:\t\t\t0x%x\n"
"\tcol:\t\t\t0x%x\n"
"\tbank:\t\t\t0x%x\n"
"\tbank group:\t\t0x%x\n"
"\trank mult:\t\t0x%x\n"
"\tchip-select:\t\t0x%x\n"
"\tsub-channel:\t\t0x%x\n",
pa, sock, die, dec.dec_umc_chan->chan_logid, dec.dec_norm_addr,
dec.dec_dimm->ud_dimmno, dec.dec_dimm_row, dec.dec_dimm_col,
dec.dec_dimm_bank, dec.dec_dimm_bank_group, dec.dec_dimm_rm,
dec.dec_dimm_csno, dec.dec_dimm_subchan);
}
static const mc_backend_t mc_backends[] = {
{ "imc", mcb_imc_init, mcb_imc_decode_pa },
{ "zen_umc", mcb_umc_init, mcb_umc_decode_pa, }
};
static void *
mcdecode_from_file(const char *file)
{
int fd, ret;
struct stat st;
void *addr;
nvlist_t *nvl;
char *driver;
if ((fd = open(file, O_RDONLY)) < 0) {
err(EXIT_FAILURE, "failed to open %s", file);
}
if (fstat(fd, &st) != 0) {
err(EXIT_FAILURE, "failed to get file information for %s",
file);
}
addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fd, 0);
if (addr == MAP_FAILED) {
err(EXIT_FAILURE, "failed to map %s", file);
}
ret = nvlist_unpack(addr, st.st_size, &nvl, 0);
if (ret != 0) {
errc(EXIT_FAILURE, ret, "failed to unpack %s", file);
}
if (munmap(addr, st.st_size) != 0) {
err(EXIT_FAILURE, "failed to unmap %s", file);
}
if (close(fd) != 0) {
err(EXIT_FAILURE, "failed to close fd for %s", file);
}
if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) {
errx(EXIT_FAILURE, "missing driver indication in dump %s",
file);
}
for (uint_t i = 0; i < ARRAY_SIZE(mc_backends); i++) {
if (strcmp(driver, mc_backends[i].mcb_name) == 0) {
void *data;
mc_cur_backend = &mc_backends[i];
data = mc_cur_backend->mcb_init(nvl, file);
nvlist_free(nvl);
return (data);
}
}
errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver);
}
static void
mcdecode_pa(const char *device, uint64_t pa)
{
int fd;
mc_encode_ioc_t ioc;
bzero(&ioc, sizeof (ioc));
ioc.mcei_pa = pa;
if ((fd = open(device, O_RDONLY)) < 0) {
err(EXIT_FAILURE, "failed to open %s", device);
}
if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) {
err(EXIT_FAILURE, "failed to issue decode ioctl");
}
if (ioc.mcei_err != 0) {
(void) fprintf(stderr, "decoding of address 0x%" PRIx64
" failed with error 0x%x\n", pa, ioc.mcei_err);
exit(EXIT_FAILURE);
}
(void) printf("Decoded physical address 0x%" PRIx64 "\n"
"\tchip:\t\t\t%u\n"
"\tdie:\t\t\t%u\n"
"\tmemory controller:\t%u\n"
"\tchannel:\t\t%u\n"
"\tchannel address\t\t0x%" PRIx64"\n"
"\tdimm:\t\t\t%u\n",
pa, ioc.mcei_chip, ioc.mcei_die, ioc.mcei_mc, ioc.mcei_chan,
ioc.mcei_chan_addr, ioc.mcei_dimm);
if (ioc.mcei_rank != UINT8_MAX) {
(void) printf("\trank:\t\t\t%u\n", ioc.mcei_rank);
}
if (ioc.mcei_row != UINT32_MAX) {
(void) printf("\trow:\t\t\t0x%x\n", ioc.mcei_row);
}
if (ioc.mcei_column != UINT32_MAX) {
(void) printf("\tcol:\t\t\t0x%x\n", ioc.mcei_column);
}
if (ioc.mcei_bank != UINT8_MAX) {
(void) printf("\tbank:\t\t\t0x%x\n", ioc.mcei_bank);
}
if (ioc.mcei_bank_group != UINT8_MAX) {
(void) printf("\tbank group:\t\t0x%x\n", ioc.mcei_bank_group);
}
if (ioc.mcei_rm != UINT8_MAX) {
(void) printf("\trank mult:\t\t0x%x\n", ioc.mcei_rm);
}
if (ioc.mcei_cs != UINT8_MAX) {
(void) printf("\tchip-select:\t\t0x%x\n", ioc.mcei_cs);
}
if (ioc.mcei_subchan != UINT8_MAX) {
(void) printf("\tsub-channel:\t\t0x%x\n", ioc.mcei_subchan);
}
(void) close(fd);
}
static void
mcdecode_dump(const char *device, const char *outfile)
{
int fd;
mc_snapshot_info_t mcs;
char *buf;
if ((fd = open(device, O_RDONLY)) < 0) {
err(EXIT_FAILURE, "failed to open %s", device);
}
bzero(&mcs, sizeof (mcs));
if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) {
err(EXIT_FAILURE, "failed to get decode snapshot information");
}
if ((buf = malloc(mcs.mcs_size)) == NULL) {
err(EXIT_FAILURE, "failed to allocate %u bytes for the "
"dump snapshot", mcs.mcs_size);
}
if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) {
err(EXIT_FAILURE, "failed to retrieve decode snapshot");
}
(void) close(fd);
if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
err(EXIT_FAILURE, "failed to create output file %s", outfile);
}
while (mcs.mcs_size > 0) {
ssize_t ret;
size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE :
mcs.mcs_size;
ret = write(fd, buf, out);
if (ret < 0) {
warn("failed to write to output file %s", outfile);
(void) unlink(outfile);
exit(EXIT_FAILURE);
}
buf += ret;
mcs.mcs_size -= ret;
}
if (fsync(fd) != 0) {
warn("failed to sync output file %s", outfile);
(void) unlink(outfile);
exit(EXIT_FAILURE);
}
(void) close(fd);
}
int
main(int argc, char *argv[])
{
int c;
uint64_t pa = UINT64_MAX;
const char *outfile = NULL;
const char *infile = NULL;
void *backend;
while ((c = getopt(argc, argv, "d:f:w:")) != -1) {
char *eptr;
unsigned long long tmp;
switch (c) {
case 'd':
errno = 0;
tmp = strtoull(optarg, &eptr, 0);
if (errno != 0 || *eptr != '\0') {
errx(EXIT_FAILURE, "failed to parse address "
"'%s'", optarg);
}
pa = (uint64_t)tmp;
break;
case 'f':
infile = optarg;
break;
case 'w':
outfile = optarg;
break;
case ':':
warnx("Option -%c requires an operand", optopt);
mcdecode_usage();
break;
case '?':
warnx("Unknown option: -%c", optopt);
mcdecode_usage();
break;
}
}
argc -= optind;
argv += optind;
if (outfile != NULL && infile != NULL) {
errx(EXIT_FAILURE, "-f and -w cannot be used together");
}
if (pa != UINT64_MAX && outfile != NULL) {
errx(EXIT_FAILURE, "-w and -d cannot be used together");
}
if (pa == UINT64_MAX && outfile == NULL) {
warnx("missing either -d or -w\n");
mcdecode_usage();
}
if (argc != 1 && infile == NULL) {
errx(EXIT_FAILURE, "missing device argument");
}
if (infile == NULL) {
if (pa != UINT64_MAX) {
mcdecode_pa(argv[0], pa);
} else {
mcdecode_dump(argv[0], outfile);
}
return (0);
}
backend = mcdecode_from_file(infile);
mc_cur_backend->mcb_decode_pa(backend, pa);
return (0);
}