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 errx(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 errx(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 ret = nvlist_unpack(addr, st.st_size, &nvl, 0); 191 if (ret != 0) { 192 errx(EXIT_FAILURE, "failed to unpack %s: %s", 193 strerror(ret)); 194 } 195 if (munmap(addr, st.st_size) != 0) { 196 err(EXIT_FAILURE, "failed to unmap %s", file); 197 } 198 if (close(fd) != 0) { 199 err(EXIT_FAILURE, "failed to close fd for %s", file); 200 } 201 202 if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) { 203 errx(EXIT_FAILURE, "missing driver indication in dump %s", 204 file); 205 } 206 207 for (uint_t i = 0; i < ARRAY_SIZE(mc_backends); i++) { 208 if (strcmp(driver, mc_backends[i].mcb_name) == 0) { 209 void *data; 210 211 mc_cur_backend = &mc_backends[i]; 212 data = mc_cur_backend->mcb_init(nvl, file); 213 nvlist_free(nvl); 214 return (data); 215 } 216 } 217 218 errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver); 219 } 220 221 static void 222 mcdecode_pa(const char *device, uint64_t pa) 223 { 224 int fd; 225 mc_encode_ioc_t ioc; 226 227 bzero(&ioc, sizeof (ioc)); 228 ioc.mcei_pa = pa; 229 230 if ((fd = open(device, O_RDONLY)) < 0) { 231 err(EXIT_FAILURE, "failed to open %s", device); 232 } 233 234 if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) { 235 err(EXIT_FAILURE, "failed to issue decode ioctl"); 236 } 237 238 if (ioc.mcei_err != 0) { 239 (void) fprintf(stderr, "decoding of address 0x%" PRIx64 240 " failed with error 0x%x\n", pa, ioc.mcei_err); 241 exit(EXIT_FAILURE); 242 } 243 244 (void) printf("Decoded physical address 0x%" PRIx64 "\n" 245 "\tchip:\t\t\t%u\n" 246 "\tdie:\t\t\t%u\n" 247 "\tmemory controller:\t%u\n" 248 "\tchannel:\t\t%u\n" 249 "\tchannel address\t\t0x%" PRIx64"\n" 250 "\tdimm:\t\t\t%u\n", 251 pa, ioc.mcei_chip, ioc.mcei_die, ioc.mcei_mc, ioc.mcei_chan, 252 ioc.mcei_chan_addr, ioc.mcei_dimm); 253 if (ioc.mcei_rank != UINT8_MAX) { 254 (void) printf("\trank:\t\t\t%u\n", ioc.mcei_rank); 255 } 256 257 if (ioc.mcei_row != UINT32_MAX) { 258 (void) printf("\trow:\t\t\t0x%x\n", ioc.mcei_row); 259 } 260 261 if (ioc.mcei_column != UINT32_MAX) { 262 (void) printf("\tcol:\t\t\t0x%x\n", ioc.mcei_column); 263 } 264 265 if (ioc.mcei_bank != UINT8_MAX) { 266 (void) printf("\tbank:\t\t\t0x%x\n", ioc.mcei_bank); 267 } 268 269 if (ioc.mcei_bank_group != UINT8_MAX) { 270 (void) printf("\tbank group:\t\t0x%x\n", ioc.mcei_bank_group); 271 } 272 273 if (ioc.mcei_rm != UINT8_MAX) { 274 (void) printf("\trank mult:\t\t0x%x\n", ioc.mcei_rm); 275 } 276 277 if (ioc.mcei_cs != UINT8_MAX) { 278 (void) printf("\tchip-select:\t\t0x%x\n", ioc.mcei_cs); 279 } 280 281 if (ioc.mcei_subchan != UINT8_MAX) { 282 (void) printf("\tsub-channel:\t\t0x%x\n", ioc.mcei_subchan); 283 } 284 285 (void) close(fd); 286 } 287 288 static void 289 mcdecode_dump(const char *device, const char *outfile) 290 { 291 int fd; 292 mc_snapshot_info_t mcs; 293 char *buf; 294 295 if ((fd = open(device, O_RDONLY)) < 0) { 296 err(EXIT_FAILURE, "failed to open %s", device); 297 } 298 299 bzero(&mcs, sizeof (mcs)); 300 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) { 301 err(EXIT_FAILURE, "failed to get decode snapshot information"); 302 } 303 304 if ((buf = malloc(mcs.mcs_size)) == NULL) { 305 err(EXIT_FAILURE, "failed to allocate %u bytes for the " 306 "dump snapshot", mcs.mcs_size); 307 } 308 309 if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) { 310 err(EXIT_FAILURE, "failed to retrieve decode snapshot"); 311 } 312 (void) close(fd); 313 314 if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) { 315 err(EXIT_FAILURE, "failed to create output file %s", outfile); 316 } 317 318 while (mcs.mcs_size > 0) { 319 ssize_t ret; 320 size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE : 321 mcs.mcs_size; 322 323 ret = write(fd, buf, out); 324 if (ret < 0) { 325 warn("failed to write to output file %s", outfile); 326 (void) unlink(outfile); 327 exit(EXIT_FAILURE); 328 } 329 330 buf += ret; 331 mcs.mcs_size -= ret; 332 } 333 334 if (fsync(fd) != 0) { 335 warn("failed to sync output file %s", outfile); 336 (void) unlink(outfile); 337 exit(EXIT_FAILURE); 338 } 339 340 (void) close(fd); 341 } 342 343 int 344 main(int argc, char *argv[]) 345 { 346 int c; 347 uint64_t pa = UINT64_MAX; 348 const char *outfile = NULL; 349 const char *infile = NULL; 350 void *backend; 351 352 while ((c = getopt(argc, argv, "d:f:w:")) != -1) { 353 char *eptr; 354 unsigned long long tmp; 355 356 switch (c) { 357 case 'd': 358 errno = 0; 359 tmp = strtoull(optarg, &eptr, 0); 360 if (errno != 0 || *eptr != '\0') { 361 errx(EXIT_FAILURE, "failed to parse address " 362 "'%s'", eptr); 363 } 364 pa = (uint64_t)tmp; 365 break; 366 case 'f': 367 infile = optarg; 368 break; 369 case 'w': 370 outfile = optarg; 371 break; 372 case ':': 373 warnx("Option -%c requires an operand", optopt); 374 mcdecode_usage(); 375 break; 376 case '?': 377 warnx("Unknown option: -%c", optopt); 378 mcdecode_usage(); 379 break; 380 } 381 } 382 383 argc -= optind; 384 argv += optind; 385 386 if (outfile != NULL && infile != NULL) { 387 errx(EXIT_FAILURE, "-f and -w cannot be used together"); 388 } 389 390 if (pa != UINT64_MAX && outfile != NULL) { 391 errx(EXIT_FAILURE, "-w and -d cannot be used together"); 392 } 393 394 if (pa == UINT64_MAX && outfile == NULL) { 395 warnx("missing either -d or -w\n"); 396 mcdecode_usage(); 397 398 } 399 400 if (argc != 1 && infile == NULL) { 401 errx(EXIT_FAILURE, "missing device argument"); 402 } 403 404 if (infile == NULL) { 405 if (pa != UINT64_MAX) { 406 mcdecode_pa(argv[0], pa); 407 } else { 408 mcdecode_dump(argv[0], outfile); 409 } 410 411 return (0); 412 } 413 414 backend = mcdecode_from_file(infile); 415 mc_cur_backend->mcb_decode_pa(backend, pa); 416 return (0); 417 } 418