diff options
Diffstat (limited to 'usr/src/cmd/fm/mcdecode/mcdecode.c')
| -rw-r--r-- | usr/src/cmd/fm/mcdecode/mcdecode.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/usr/src/cmd/fm/mcdecode/mcdecode.c b/usr/src/cmd/fm/mcdecode/mcdecode.c new file mode 100644 index 0000000000..e6b8b62ce7 --- /dev/null +++ b/usr/src/cmd/fm/mcdecode/mcdecode.c @@ -0,0 +1,284 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * Command utility to drive synthetic memory decoding. + */ + +#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/mc.h> +#include "imc.h" + +#define MCDECODE_USAGE 2 + +/* + * Write in 32k chunks. + */ +#define MCDECODE_WRITE (1024 * 32) + +static void +mcdecode_usage(void) +{ + (void) fprintf(stderr, + "Usage: mcdecode [-f infile] [-d address | -w outfile] device\n" + "\n" + "\t-d decode physical address to the correspond 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 +mcdecode_from_file(const char *file, uint64_t pa) +{ + int fd, ret; + struct stat st; + void *addr; + nvlist_t *nvl; + imc_t imc; + imc_decode_state_t dec; + 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) { + errx(EXIT_FAILURE, "failed to unpack %s: %s", + strerror(ret)); + } + 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); + } + + if (strcmp(driver, "imc") != 0) { + errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver); + } + + if (!imc_restore_decoder(nvl, &imc)) { + errx(EXIT_FAILURE, "failed to restore memory controller " + "snapshot in %s", file); + } + + bzero(&dec, sizeof (dec)); + + if (!imc_decode_pa(&imc, pa, &dec)) { + errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64, pa); + } + + (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); + + nvlist_free(nvl); +} + +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" + "\tmemory controller:\t%u\n" + "\tchannel:\t\t%u\n" + "\tdimm:\t\t\t%u\n" + "\trank:\t\t\t%u\n", + pa, ioc.mcei_chip, ioc.mcei_mc, ioc.mcei_chan, ioc.mcei_dimm, + ioc.mcei_rank); + + (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; + + 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'", eptr); + } + 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 (pa != UINT64_MAX) { + if (infile != NULL) { + mcdecode_from_file(infile, pa); + } else { + mcdecode_pa(argv[0], pa); + } + } else { + mcdecode_dump(argv[0], outfile); + } + return (0); +} |
