1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2019 Joyent, Inc. 14 */ 15 16 /* 17 * Command utility to drive synthetic memory decoding. 18 */ 19 20 #include <stdio.h> 21 #include <errno.h> 22 #include <stdlib.h> 23 #include <err.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 #include <strings.h> 28 #include <unistd.h> 29 #include <sys/mman.h> 30 #include <libnvpair.h> 31 32 #include <sys/mc.h> 33 #include "imc.h" 34 35 #define MCDECODE_USAGE 2 36 37 /* 38 * Write in 32k chunks. 39 */ 40 #define MCDECODE_WRITE (1024 * 32) 41 42 static void 43 mcdecode_usage(void) 44 { 45 (void) fprintf(stderr, 46 "Usage: mcdecode [-f infile] [-d address | -w outfile] device\n" 47 "\n" 48 "\t-d decode physical address to the correspond dimm\n" 49 "\t-f use decoder image from infile\n" 50 "\t-w write decoder snapshot state to the specified file\n"); 51 exit(MCDECODE_USAGE); 52 } 53 54 static void 55 mcdecode_from_file(const char *file, uint64_t pa) 56 { 57 int fd, ret; 58 struct stat st; 59 void *addr; 60 nvlist_t *nvl; 61 imc_t imc; 62 imc_decode_state_t dec; 63 char *driver; 64 65 if ((fd = open(file, O_RDONLY)) < 0) { 66 err(EXIT_FAILURE, "failed to open %s", file); 67 } 68 69 if (fstat(fd, &st) != 0) { 70 err(EXIT_FAILURE, "failed to get file information for %s", 71 file); 72 } 73 74 addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 75 fd, 0); 76 if (addr == MAP_FAILED) { 77 err(EXIT_FAILURE, "failed to map %s", file); 78 } 79 ret = nvlist_unpack(addr, st.st_size, &nvl, 0); 80 if (ret != 0) { 81 errx(EXIT_FAILURE, "failed to unpack %s: %s", 82 strerror(ret)); 83 } 84 if (munmap(addr, st.st_size) != 0) { 85 err(EXIT_FAILURE, "failed to unmap %s", file); 86 } 87 if (close(fd) != 0) { 88 err(EXIT_FAILURE, "failed to close fd for %s", file); 89 } 90 91 if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) { 92 errx(EXIT_FAILURE, "missing driver indication in dump %s", 93 file); 94 } 95 96 if (strcmp(driver, "imc") != 0) { 97 errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver); 98 } 99 100 if (!imc_restore_decoder(nvl, &imc)) { 101 errx(EXIT_FAILURE, "failed to restore memory controller " 102 "snapshot in %s", file); 103 } 104 105 bzero(&dec, sizeof (dec)); 106 107 if (!imc_decode_pa(&imc, pa, &dec)) { 108 errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64, pa); 109 } 110 111 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 112 "\tchip:\t\t\t%u\n" 113 "\tmemory controller:\t%u\n" 114 "\tchannel:\t\t%u\n" 115 "\tdimm:\t\t\t%u\n" 116 "\trank:\t\t\t%u\n", 117 pa, dec.ids_nodeid, dec.ids_tadid, dec.ids_channelid, 118 dec.ids_dimmid, dec.ids_rankid); 119 120 nvlist_free(nvl); 121 } 122 123 static void 124 mcdecode_pa(const char *device, uint64_t pa) 125 { 126 int fd; 127 mc_encode_ioc_t ioc; 128 129 bzero(&ioc, sizeof (ioc)); 130 ioc.mcei_pa = pa; 131 132 if ((fd = open(device, O_RDONLY)) < 0) { 133 err(EXIT_FAILURE, "failed to open %s", device); 134 } 135 136 if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) { 137 err(EXIT_FAILURE, "failed to issue decode ioctl"); 138 } 139 140 if (ioc.mcei_err != 0) { 141 (void) fprintf(stderr, "decoding of address 0x%" PRIx64 142 " failed with error 0x%x\n", pa, ioc.mcei_err); 143 exit(EXIT_FAILURE); 144 } 145 146 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 147 "\tchip:\t\t\t%u\n" 148 "\tmemory controller:\t%u\n" 149 "\tchannel:\t\t%u\n" 150 "\tdimm:\t\t\t%u\n" 151 "\trank:\t\t\t%u\n", 152 pa, ioc.mcei_chip, ioc.mcei_mc, ioc.mcei_chan, ioc.mcei_dimm, 153 ioc.mcei_rank); 154 155 (void) close(fd); 156 } 157 158 static void 159 mcdecode_dump(const char *device, const char *outfile) 160 { 161 int fd; 162 mc_snapshot_info_t mcs; 163 char *buf; 164 165 if ((fd = open(device, O_RDONLY)) < 0) { 166 err(EXIT_FAILURE, "failed to open %s", device); 167 } 168 169 bzero(&mcs, sizeof (mcs)); 170 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) { 171 err(EXIT_FAILURE, "failed to get decode snapshot information"); 172 } 173 174 if ((buf = malloc(mcs.mcs_size)) == NULL) { 175 err(EXIT_FAILURE, "failed to allocate %u bytes for the " 176 "dump snapshot", mcs.mcs_size); 177 } 178 179 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) { 180 err(EXIT_FAILURE, "failed to retrieve decode snapshot"); 181 } 182 (void) close(fd); 183 184 if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) { 185 err(EXIT_FAILURE, "failed to create output file %s", outfile); 186 } 187 188 while (mcs.mcs_size > 0) { 189 ssize_t ret; 190 size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE : 191 mcs.mcs_size; 192 193 ret = write(fd, buf, out); 194 if (ret < 0) { 195 warn("failed to write to output file %s", outfile); 196 (void) unlink(outfile); 197 exit(EXIT_FAILURE); 198 } 199 200 buf += ret; 201 mcs.mcs_size -= ret; 202 } 203 204 if (fsync(fd) != 0) { 205 warn("failed to sync output file %s", outfile); 206 (void) unlink(outfile); 207 exit(EXIT_FAILURE); 208 } 209 210 (void) close(fd); 211 } 212 213 int 214 main(int argc, char *argv[]) 215 { 216 int c; 217 uint64_t pa = UINT64_MAX; 218 const char *outfile = NULL; 219 const char *infile = NULL; 220 221 while ((c = getopt(argc, argv, "d:f:w:")) != -1) { 222 char *eptr; 223 unsigned long long tmp; 224 225 switch (c) { 226 case 'd': 227 errno = 0; 228 tmp = strtoull(optarg, &eptr, 0); 229 if (errno != 0 || *eptr != '\0') { 230 errx(EXIT_FAILURE, "failed to parse address " 231 "'%s'", eptr); 232 } 233 pa = (uint64_t)tmp; 234 break; 235 case 'f': 236 infile = optarg; 237 break; 238 case 'w': 239 outfile = optarg; 240 break; 241 case ':': 242 warnx("Option -%c requires an operand", optopt); 243 mcdecode_usage(); 244 break; 245 case '?': 246 warnx("Unknown option: -%c", optopt); 247 mcdecode_usage(); 248 break; 249 } 250 } 251 252 argc -= optind; 253 argv += optind; 254 255 if (outfile != NULL && infile != NULL) { 256 errx(EXIT_FAILURE, "-f and -w cannot be used together"); 257 } 258 259 if (pa != UINT64_MAX && outfile != NULL) { 260 errx(EXIT_FAILURE, "-w and -d cannot be used together"); 261 } 262 263 if (pa == UINT64_MAX && outfile == NULL) { 264 warnx("missing either -d or -w\n"); 265 mcdecode_usage(); 266 267 } 268 269 if (argc != 1 && infile == NULL) { 270 errx(EXIT_FAILURE, "missing device argument"); 271 } 272 273 274 if (pa != UINT64_MAX) { 275 if (infile != NULL) { 276 mcdecode_from_file(infile, pa); 277 } else { 278 mcdecode_pa(argv[0], pa); 279 } 280 } else { 281 mcdecode_dump(argv[0], outfile); 282 } 283 return (0); 284 } 285