xref: /illumos-gate/usr/src/cmd/fm/mcdecode/mcdecode.c (revision eb00b1c8)
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  */
15 
16 /*
17  * Command utility to drive synthetic memory decoding.
18  */
19 
20 #include <stdio.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <err.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <strings.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <libnvpair.h>
31 
32 #include <sys/mc.h>
33 #include "imc.h"
34 
35 #define	MCDECODE_USAGE	2
36 
37 /*
38  * Write in 32k chunks.
39  */
40 #define	MCDECODE_WRITE	(1024 * 32)
41 
42 static void
mcdecode_usage(void)43 mcdecode_usage(void)
44 {
45 	(void) fprintf(stderr,
46 	    "Usage: mcdecode [-f infile] [-d address | -w outfile] device\n"
47 	    "\n"
48 	    "\t-d  decode physical address to the correspond dimm\n"
49 	    "\t-f  use decoder image from infile\n"
50 	    "\t-w  write decoder snapshot state to the specified file\n");
51 	exit(MCDECODE_USAGE);
52 }
53 
54 static void
mcdecode_from_file(const char * file,uint64_t pa)55 mcdecode_from_file(const char *file, uint64_t pa)
56 {
57 	int fd, ret;
58 	struct stat st;
59 	void *addr;
60 	nvlist_t *nvl;
61 	imc_t imc;
62 	imc_decode_state_t dec;
63 	char *driver;
64 
65 	if ((fd = open(file, O_RDONLY)) < 0) {
66 		err(EXIT_FAILURE, "failed to open %s", file);
67 	}
68 
69 	if (fstat(fd, &st) != 0) {
70 		err(EXIT_FAILURE, "failed to get file information for %s",
71 		    file);
72 	}
73 
74 	addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
75 	    fd, 0);
76 	if (addr == MAP_FAILED) {
77 		err(EXIT_FAILURE, "failed to map %s", file);
78 	}
79 	ret = nvlist_unpack(addr, st.st_size, &nvl, 0);
80 	if (ret != 0) {
81 		errx(EXIT_FAILURE, "failed to unpack %s: %s",
82 		    strerror(ret));
83 	}
84 	if (munmap(addr, st.st_size) != 0) {
85 		err(EXIT_FAILURE, "failed to unmap %s", file);
86 	}
87 	if (close(fd) != 0) {
88 		err(EXIT_FAILURE, "failed to close fd for %s", file);
89 	}
90 
91 	if (nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0) {
92 		errx(EXIT_FAILURE, "missing driver indication in dump %s",
93 		    file);
94 	}
95 
96 	if (strcmp(driver, "imc") != 0) {
97 		errx(EXIT_FAILURE, "unknown driver dump source %s\n", driver);
98 	}
99 
100 	if (!imc_restore_decoder(nvl, &imc)) {
101 		errx(EXIT_FAILURE, "failed to restore memory controller "
102 		    "snapshot in %s", file);
103 	}
104 
105 	bzero(&dec, sizeof (dec));
106 
107 	if (!imc_decode_pa(&imc, pa, &dec)) {
108 		errx(EXIT_FAILURE, "failed to decode address 0x%" PRIx64, pa);
109 	}
110 
111 	(void) printf("Decoded physical address 0x%" PRIx64 "\n"
112 	    "\tchip:\t\t\t%u\n"
113 	    "\tmemory controller:\t%u\n"
114 	    "\tchannel:\t\t%u\n"
115 	    "\tdimm:\t\t\t%u\n"
116 	    "\trank:\t\t\t%u\n",
117 	    pa, dec.ids_nodeid, dec.ids_tadid, dec.ids_channelid,
118 	    dec.ids_dimmid, dec.ids_rankid);
119 
120 	nvlist_free(nvl);
121 }
122 
123 static void
mcdecode_pa(const char * device,uint64_t pa)124 mcdecode_pa(const char *device, uint64_t pa)
125 {
126 	int fd;
127 	mc_encode_ioc_t ioc;
128 
129 	bzero(&ioc, sizeof (ioc));
130 	ioc.mcei_pa = pa;
131 
132 	if ((fd = open(device, O_RDONLY)) < 0) {
133 		err(EXIT_FAILURE, "failed to open %s", device);
134 	}
135 
136 	if (ioctl(fd, MC_IOC_DECODE_PA, &ioc) != 0) {
137 		err(EXIT_FAILURE, "failed to issue decode ioctl");
138 	}
139 
140 	if (ioc.mcei_err != 0) {
141 		(void) fprintf(stderr, "decoding of address 0x%" PRIx64
142 		    " failed with error 0x%x\n", pa, ioc.mcei_err);
143 		exit(EXIT_FAILURE);
144 	}
145 
146 	(void) printf("Decoded physical address 0x%" PRIx64 "\n"
147 	    "\tchip:\t\t\t%u\n"
148 	    "\tmemory controller:\t%u\n"
149 	    "\tchannel:\t\t%u\n"
150 	    "\tdimm:\t\t\t%u\n"
151 	    "\trank:\t\t\t%u\n",
152 	    pa, ioc.mcei_chip, ioc.mcei_mc, ioc.mcei_chan, ioc.mcei_dimm,
153 	    ioc.mcei_rank);
154 
155 	(void) close(fd);
156 }
157 
158 static void
mcdecode_dump(const char * device,const char * outfile)159 mcdecode_dump(const char *device, const char *outfile)
160 {
161 	int fd;
162 	mc_snapshot_info_t mcs;
163 	char *buf;
164 
165 	if ((fd = open(device, O_RDONLY)) < 0) {
166 		err(EXIT_FAILURE, "failed to open %s", device);
167 	}
168 
169 	bzero(&mcs, sizeof (mcs));
170 	if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT_INFO, &mcs) != 0) {
171 		err(EXIT_FAILURE, "failed to get decode snapshot information");
172 	}
173 
174 	if ((buf = malloc(mcs.mcs_size)) == NULL) {
175 		err(EXIT_FAILURE, "failed to allocate %u bytes for the "
176 		    "dump snapshot", mcs.mcs_size);
177 	}
178 
179 	if (ioctl(fd, MC_IOC_DECODE_SNAPSHOT, buf) != 0) {
180 		err(EXIT_FAILURE, "failed to retrieve decode snapshot");
181 	}
182 	(void) close(fd);
183 
184 	if ((fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
185 		err(EXIT_FAILURE, "failed to create output file %s", outfile);
186 	}
187 
188 	while (mcs.mcs_size > 0) {
189 		ssize_t ret;
190 		size_t out = mcs.mcs_size > MCDECODE_WRITE ? MCDECODE_WRITE :
191 		    mcs.mcs_size;
192 
193 		ret = write(fd, buf, out);
194 		if (ret < 0) {
195 			warn("failed to write to output file %s", outfile);
196 			(void) unlink(outfile);
197 			exit(EXIT_FAILURE);
198 		}
199 
200 		buf += ret;
201 		mcs.mcs_size -= ret;
202 	}
203 
204 	if (fsync(fd) != 0) {
205 		warn("failed to sync output file %s", outfile);
206 		(void) unlink(outfile);
207 		exit(EXIT_FAILURE);
208 	}
209 
210 	(void) close(fd);
211 }
212 
213 int
main(int argc,char * argv[])214 main(int argc, char *argv[])
215 {
216 	int c;
217 	uint64_t pa = UINT64_MAX;
218 	const char *outfile = NULL;
219 	const char *infile = NULL;
220 
221 	while ((c = getopt(argc, argv, "d:f:w:")) != -1) {
222 		char *eptr;
223 		unsigned long long tmp;
224 
225 		switch (c) {
226 		case 'd':
227 			errno = 0;
228 			tmp = strtoull(optarg, &eptr, 0);
229 			if (errno != 0 || *eptr != '\0') {
230 				errx(EXIT_FAILURE, "failed to parse address "
231 				    "'%s'", eptr);
232 			}
233 			pa = (uint64_t)tmp;
234 			break;
235 		case 'f':
236 			infile = optarg;
237 			break;
238 		case 'w':
239 			outfile = optarg;
240 			break;
241 		case ':':
242 			warnx("Option -%c requires an operand", optopt);
243 			mcdecode_usage();
244 			break;
245 		case '?':
246 			warnx("Unknown option: -%c", optopt);
247 			mcdecode_usage();
248 			break;
249 		}
250 	}
251 
252 	argc -= optind;
253 	argv += optind;
254 
255 	if (outfile != NULL && infile != NULL) {
256 		errx(EXIT_FAILURE, "-f and -w cannot be used together");
257 	}
258 
259 	if (pa != UINT64_MAX && outfile != NULL) {
260 		errx(EXIT_FAILURE, "-w and -d cannot be used together");
261 	}
262 
263 	if (pa == UINT64_MAX && outfile == NULL) {
264 		warnx("missing either -d or -w\n");
265 		mcdecode_usage();
266 
267 	}
268 
269 	if (argc != 1 && infile == NULL) {
270 		errx(EXIT_FAILURE, "missing device argument");
271 	}
272 
273 
274 	if (pa != UINT64_MAX) {
275 		if (infile != NULL) {
276 			mcdecode_from_file(infile, pa);
277 		} else {
278 			mcdecode_pa(argv[0], pa);
279 		}
280 	} else {
281 		mcdecode_dump(argv[0], outfile);
282 	}
283 	return (0);
284 }
285