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