xref: /illumos-gate/usr/src/cmd/fm/mcdecode/mcdecode.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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