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
mcdecode_usage(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 *
mcb_imc_init(nvlist_t * nvl,const char * file)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
mcb_imc_decode_pa(void * arg,uint64_t pa)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 *
mcb_umc_init(nvlist_t * nvl,const char * file)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
mcb_umc_decode_pa(void * arg,uint64_t pa)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 *
mcdecode_from_file(const char * file)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
mcdecode_pa(const char * device,uint64_t pa)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
mcdecode_dump(const char * device,const char * outfile)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
main(int argc,char * argv[])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