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 * Copyright 2022 Oxide Computer Company 15 */ 16 17 /* 18 * Command utility to drive synthetic memory decoding. 19 */ 20 21 #include <stdio.h> 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <err.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <fcntl.h> 28 #include <strings.h> 29 #include <unistd.h> 30 #include <sys/mman.h> 31 #include <libnvpair.h> 32 #include <sys/sysmacros.h> 33 34 #include <sys/mc.h> 35 #include "imc.h" 36 #include "zen_umc.h" 37 38 #define MCDECODE_USAGE 2 39 40 /* 41 * Write in 32k chunks. 42 */ 43 #define MCDECODE_WRITE (1024 * 32) 44 45 typedef struct mc_backend { 46 const char *mcb_name; 47 void *(*mcb_init)(nvlist_t *, const char *); 48 void (*mcb_decode_pa)(void *, uint64_t); 49 } mc_backend_t; 50 51 static const mc_backend_t *mc_cur_backend = NULL; 52 53 static void 54 mcdecode_usage(void) 55 { 56 (void) fprintf(stderr, 57 "Usage: mcdecode -d address -f infile | device\n" 58 " mcdecode -w outfile device\n" 59 "\n" 60 "\t-d decode physical address to the corresponding dimm\n" 61 "\t-f use decoder image from infile\n" 62 "\t-w write decoder snapshot state to the specified file\n"); 63 exit(MCDECODE_USAGE); 64 } 65 66 static void * 67 mcb_imc_init(nvlist_t *nvl, const char *file) 68 { 69 imc_t *imc; 70 71 imc = calloc(1, sizeof (*imc)); 72 if (imc == NULL) { 73 err(EXIT_FAILURE, "failed to allocate memory for imc_t"); 74 } 75 76 if (!imc_restore_decoder(nvl, imc)) { 77 errx(EXIT_FAILURE, "failed to restore memory " 78 "controller snapshot in %s", file); 79 } 80 81 return (imc); 82 } 83 84 static void 85 mcb_imc_decode_pa(void *arg, uint64_t pa) 86 { 87 const imc_t *imc = arg; 88 imc_decode_state_t dec; 89 90 bzero(&dec, sizeof (dec)); 91 if (!imc_decode_pa(imc, pa, &dec)) { 92 errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64 93 " -- 0x%x, 0x%" PRIx64, pa, dec.ids_fail, 94 dec.ids_fail_data); 95 } 96 97 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 98 "\tchip:\t\t\t%u\n" 99 "\tmemory controller:\t%u\n" 100 "\tchannel:\t\t%u\n" 101 "\tdimm:\t\t\t%u\n" 102 "\trank:\t\t\t%u\n", 103 pa, dec.ids_nodeid, dec.ids_tadid, dec.ids_channelid, 104 dec.ids_dimmid, dec.ids_rankid); 105 } 106 107 static void * 108 mcb_umc_init(nvlist_t *nvl, const char *file) 109 { 110 zen_umc_t *umc; 111 112 umc = calloc(1, sizeof (*umc)); 113 if (umc == NULL) { 114 err(EXIT_FAILURE, "failed to allocate memory for zen_umc_t"); 115 } 116 117 if (!zen_umc_restore_decoder(nvl, umc)) { 118 errx(EXIT_FAILURE, "failed to restore memory " 119 "controller snapshot in %s", file); 120 } 121 122 return (umc); 123 } 124 125 126 static void 127 mcb_umc_decode_pa(void *arg, uint64_t pa) 128 { 129 zen_umc_t *umc = arg; 130 zen_umc_decoder_t dec; 131 uint32_t sock, die, comp; 132 133 bzero(&dec, sizeof (dec)); 134 if (!zen_umc_decode_pa(umc, pa, &dec)) { 135 errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64 136 " -- 0x%x, 0x%" PRIx64, pa, dec.dec_fail, 137 dec.dec_fail_data); 138 } 139 140 zen_fabric_id_decompose(&umc->umc_decomp, dec.dec_targ_fabid, &sock, 141 &die, &comp); 142 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 143 "\tsocket:\t\t\t%u\n" 144 "\tdie:\t\t\t%u\n" 145 "\tchannel:\t\t%u\n" 146 "\tchannel address\t\t0x%" PRIx64 "\n" 147 "\tdimm:\t\t\t%u\n" 148 "\trow:\t\t\t0x%x\n" 149 "\tcol:\t\t\t0x%x\n" 150 "\tbank:\t\t\t0x%x\n" 151 "\tbank group:\t\t0x%x\n" 152 "\trank mult:\t\t0x%x\n" 153 "\tchip-select:\t\t0x%x\n" 154 "\tsub-channel:\t\t0x%x\n", 155 pa, sock, die, dec.dec_umc_chan->chan_logid, dec.dec_norm_addr, 156 dec.dec_dimm->ud_dimmno, dec.dec_dimm_row, dec.dec_dimm_col, 157 dec.dec_dimm_bank, dec.dec_dimm_bank_group, dec.dec_dimm_rm, 158 dec.dec_dimm_csno, dec.dec_dimm_subchan); 159 160 } 161 162 static const mc_backend_t mc_backends[] = { 163 { "imc", mcb_imc_init, mcb_imc_decode_pa }, 164 { "zen_umc", mcb_umc_init, mcb_umc_decode_pa, } 165 }; 166 167 static void * 168 mcdecode_from_file(const char *file) 169 { 170 int fd, ret; 171 struct stat st; 172 void *addr; 173 nvlist_t *nvl; 174 char *driver; 175 176 if ((fd = open(file, O_RDONLY)) < 0) { 177 err(EXIT_FAILURE, "failed to open %s", file); 178 } 179 180 if (fstat(fd, &st) != 0) { 181 err(EXIT_FAILURE, "failed to get file information for %s", 182 file); 183 } 184 185 addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 186 fd, 0); 187 if (addr == MAP_FAILED) { 188 err(EXIT_FAILURE, "failed to map %s", file); 189 } 190 191 ret = nvlist_unpack(addr, st.st_size, &nvl, 0); 192 if (ret != 0) { 193 errc(EXIT_FAILURE, ret, "failed to unpack %s", file); 194 } 195 196 if (munmap(addr, st.st_size) != 0) { 197 err(EXIT_FAILURE, "failed to unmap %s", file); 198 } 199 200 if (close(fd) != 0) { 201 err(EXIT_FAILURE, "failed to close fd for %s", file); 202 } 203 204 if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) { 205 errx(EXIT_FAILURE, "missing driver indication in dump %s", 206 file); 207 } 208 209 for (uint_t i = 0; i < ARRAY_SIZE(mc_backends); i++) { 210 if (strcmp(driver, mc_backends[i].mcb_name) == 0) { 211 void *data; 212 213 mc_cur_backend = &mc_backends[i]; 214 data = mc_cur_backend->mcb_init(nvl, file); 215 nvlist_free(nvl); 216 return (data); 217 } 218 } 219 220 errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver); 221 } 222 223 static void 224 mcdecode_pa(const char *device, uint64_t pa) 225 { 226 int fd; 227 mc_encode_ioc_t ioc; 228 229 bzero(&ioc, sizeof (ioc)); 230 ioc.mcei_pa = pa; 231 232 if ((fd = open(device, O_RDONLY)) < 0) { 233 err(EXIT_FAILURE, "failed to open %s", device); 234 } 235 236 if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) { 237 err(EXIT_FAILURE, "failed to issue decode ioctl"); 238 } 239 240 if (ioc.mcei_err != 0) { 241 (void) fprintf(stderr, "decoding of address 0x%" PRIx64 242 " failed with error 0x%x\n", pa, ioc.mcei_err); 243 exit(EXIT_FAILURE); 244 } 245 246 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 247 "\tchip:\t\t\t%u\n" 248 "\tdie:\t\t\t%u\n" 249 "\tmemory controller:\t%u\n" 250 "\tchannel:\t\t%u\n" 251 "\tchannel address\t\t0x%" PRIx64"\n" 252 "\tdimm:\t\t\t%u\n", 253 pa, ioc.mcei_chip, ioc.mcei_die, ioc.mcei_mc, ioc.mcei_chan, 254 ioc.mcei_chan_addr, ioc.mcei_dimm); 255 if (ioc.mcei_rank != UINT8_MAX) { 256 (void) printf("\trank:\t\t\t%u\n", ioc.mcei_rank); 257 } 258 259 if (ioc.mcei_row != UINT32_MAX) { 260 (void) printf("\trow:\t\t\t0x%x\n", ioc.mcei_row); 261 } 262 263 if (ioc.mcei_column != UINT32_MAX) { 264 (void) printf("\tcol:\t\t\t0x%x\n", ioc.mcei_column); 265 } 266 267 if (ioc.mcei_bank != UINT8_MAX) { 268 (void) printf("\tbank:\t\t\t0x%x\n", ioc.mcei_bank); 269 } 270 271 if (ioc.mcei_bank_group != UINT8_MAX) { 272 (void) printf("\tbank group:\t\t0x%x\n", ioc.mcei_bank_group); 273 } 274 275 if (ioc.mcei_rm != UINT8_MAX) { 276 (void) printf("\trank mult:\t\t0x%x\n", ioc.mcei_rm); 277 } 278 279 if (ioc.mcei_cs != UINT8_MAX) { 280 (void) printf("\tchip-select:\t\t0x%x\n", ioc.mcei_cs); 281 } 282 283 if (ioc.mcei_subchan != UINT8_MAX) { 284 (void) printf("\tsub-channel:\t\t0x%x\n", ioc.mcei_subchan); 285 } 286 287 (void) close(fd); 288 } 289 290 static void 291 mcdecode_dump(const char *device, const char *outfile) 292 { 293 int fd; 294 mc_snapshot_info_t mcs; 295 char *buf; 296 297 if ((fd = open(device, O_RDONLY)) < 0) { 298 err(EXIT_FAILURE, "failed to open %s", device); 299 } 300 301 bzero(&mcs, sizeof (mcs)); 302 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) { 303 err(EXIT_FAILURE, "failed to get decode snapshot information"); 304 } 305 306 if ((buf = malloc(mcs.mcs_size)) == NULL) { 307 err(EXIT_FAILURE, "failed to allocate %u bytes for the " 308 "dump snapshot", mcs.mcs_size); 309 } 310 311 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) { 312 err(EXIT_FAILURE, "failed to retrieve decode snapshot"); 313 } 314 (void) close(fd); 315 316 if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) { 317 err(EXIT_FAILURE, "failed to create output file %s", outfile); 318 } 319 320 while (mcs.mcs_size > 0) { 321 ssize_t ret; 322 size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE : 323 mcs.mcs_size; 324 325 ret = write(fd, buf, out); 326 if (ret < 0) { 327 warn("failed to write to output file %s", outfile); 328 (void) unlink(outfile); 329 exit(EXIT_FAILURE); 330 } 331 332 buf += ret; 333 mcs.mcs_size -= ret; 334 } 335 336 if (fsync(fd) != 0) { 337 warn("failed to sync output file %s", outfile); 338 (void) unlink(outfile); 339 exit(EXIT_FAILURE); 340 } 341 342 (void) close(fd); 343 } 344 345 int 346 main(int argc, char *argv[]) 347 { 348 int c; 349 uint64_t pa = UINT64_MAX; 350 const char *outfile = NULL; 351 const char *infile = NULL; 352 void *backend; 353 354 while ((c = getopt(argc, argv, "d:f:w:")) != -1) { 355 char *eptr; 356 unsigned long long tmp; 357 358 switch (c) { 359 case 'd': 360 errno = 0; 361 tmp = strtoull(optarg, &eptr, 0); 362 if (errno != 0 || *eptr != '\0') { 363 errx(EXIT_FAILURE, "failed to parse address " 364 "'%s'", optarg); 365 } 366 pa = (uint64_t)tmp; 367 break; 368 case 'f': 369 infile = optarg; 370 break; 371 case 'w': 372 outfile = optarg; 373 break; 374 case ':': 375 warnx("Option -%c requires an operand", optopt); 376 mcdecode_usage(); 377 break; 378 case '?': 379 warnx("Unknown option: -%c", optopt); 380 mcdecode_usage(); 381 break; 382 } 383 } 384 385 argc -= optind; 386 argv += optind; 387 388 if (outfile != NULL && infile != NULL) { 389 errx(EXIT_FAILURE, "-f and -w cannot be used together"); 390 } 391 392 if (pa != UINT64_MAX && outfile != NULL) { 393 errx(EXIT_FAILURE, "-w and -d cannot be used together"); 394 } 395 396 if (pa == UINT64_MAX && outfile == NULL) { 397 warnx("missing either -d or -w\n"); 398 mcdecode_usage(); 399 400 } 401 402 if (argc != 1 && infile == NULL) { 403 errx(EXIT_FAILURE, "missing device argument"); 404 } 405 406 if (infile == NULL) { 407 if (pa != UINT64_MAX) { 408 mcdecode_pa(argv[0], pa); 409 } else { 410 mcdecode_dump(argv[0], outfile); 411 } 412 413 return (0); 414 } 415 416 backend = mcdecode_from_file(infile); 417 mc_cur_backend->mcb_decode_pa(backend, pa); 418 return (0); 419 } 420