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