xref: /freebsd/sbin/nvmecontrol/firmware.c (revision f0157ce528a128e2abb181a5c766033a2ce49a5f)
1 /*-
2  * Copyright (c) 2013 EMC Corp.
3  * All rights reserved.
4  *
5  * Copyright (C) 2012-2013 Intel Corporation
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/ioccom.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 
38 #include <ctype.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdbool.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48 
49 #include "nvmecontrol.h"
50 
51 static int
52 slot_has_valid_firmware(int fd, int slot)
53 {
54 	struct nvme_firmware_page	fw;
55 	int				has_fw = false;
56 
57 	read_logpage(fd, NVME_LOG_FIRMWARE_SLOT,
58 	    NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw));
59 
60 	if (fw.revision[slot-1] != 0LLU)
61 		has_fw = true;
62 
63 	return (has_fw);
64 }
65 
66 static void
67 read_image_file(char *path, void **buf, ssize_t *size)
68 {
69 	struct stat	sb;
70 	int		fd;
71 
72 	*size = 0;
73 	*buf = NULL;
74 
75 	if ((fd = open(path, O_RDONLY)) < 0) {
76 		fprintf(stderr, "Unable to open '%s'.\n", path);
77 		exit(EX_IOERR);
78 	}
79 	if (fstat(fd, &sb) < 0) {
80 		fprintf(stderr, "Unable to stat '%s'.\n", path);
81 		close(fd);
82 		exit(EX_IOERR);
83 	}
84 	if ((*buf = malloc(sb.st_size)) == NULL) {
85 		fprintf(stderr, "Unable to malloc %jd bytes.\n",
86 		    sb.st_size);
87 		close(fd);
88 		exit(EX_IOERR);
89 	}
90 	if ((*size = read(fd, *buf, sb.st_size)) < 0) {
91 		fprintf(stderr, "Error reading '%s', errno=%d (%s)\n",
92 		    path, errno, strerror(errno));
93 		close(fd);
94 		exit(EX_IOERR);
95 	}
96 	if (*size != sb.st_size) {
97 		fprintf(stderr, "Error reading '%s', "
98 		    "read %zd bytes, requested %jd bytes\n",
99 		    path, *size, sb.st_size);
100 		close(fd);
101 		exit(EX_IOERR);
102 	}
103 }
104 
105 static void
106 update_firmware(int fd, uint8_t *payload, uint32_t payload_size)
107 {
108 	struct nvme_pt_command	pt;
109 	size_t			size;
110 	void			*chunk;
111 	uint32_t		off, resid;
112 	int			exit_code = EX_OK;
113 
114 	off = 0;
115 	resid = payload_size;
116 
117 	if ((chunk = malloc((size_t)NVME_MAX_XFER_SIZE)) == NULL) {
118 		printf("Unable to malloc %d bytes.\n", NVME_MAX_XFER_SIZE);
119 		exit(EX_IOERR);
120 	}
121 
122 	while (resid > 0) {
123 		size = (resid >= NVME_MAX_XFER_SIZE) ?
124 		    NVME_MAX_XFER_SIZE : resid;
125 		memcpy(chunk, payload + off, size);
126 
127 		memset(&pt, 0, sizeof(pt));
128 		pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
129 		pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1;
130 		pt.cmd.cdw11 = (off / sizeof(uint32_t));
131 		pt.buf = chunk;
132 		pt.len = size;
133 		pt.is_read = 0;
134 
135 		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) {
136 			printf("Firmware image download request failed. "
137 			    "errno=%d (%s)\n",
138 			    errno, strerror(errno));
139 			exit_code = EX_IOERR;
140 			break;
141 		}
142 
143 		if (nvme_completion_is_error(&pt.cpl)) {
144 			printf("Passthrough command returned error.\n");
145 			exit_code = EX_IOERR;
146 			break;
147 		}
148 
149 		resid -= size;
150 		off += size;
151 	}
152 
153 	if (exit_code != EX_OK)
154 		exit(exit_code);
155 }
156 
157 static void
158 activate_firmware(int fd, int slot, int activate_action)
159 {
160 	struct nvme_pt_command	pt;
161 
162 	memset(&pt, 0, sizeof(pt));
163 	pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE;
164 	pt.cmd.cdw10 = (activate_action << 3) | slot;
165 	pt.is_read = 0;
166 
167 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) {
168 		printf("Firmware activate request failed. errno=%d (%s)\n",
169 		    errno, strerror(errno));
170 		exit(EX_IOERR);
171 	}
172 
173 	if (nvme_completion_is_error(&pt.cpl)) {
174 		printf("Passthrough command returned error.\n");
175 		exit(EX_IOERR);
176 	}
177 }
178 
179 static void
180 firmware_usage(void)
181 {
182 	fprintf(stderr, "usage:\n");
183 	fprintf(stderr, FIRMWARE_USAGE);
184 	exit(EX_USAGE);
185 }
186 
187 void
188 firmware(int argc, char *argv[])
189 {
190 	int				fd = -1, slot = 0;
191 	int				a_flag, s_flag, f_flag;
192 	char				ch, *p, *image = NULL;
193 	char				*controller = NULL, prompt[64];
194 	void				*buf = NULL;
195 	ssize_t				size;
196 	struct nvme_controller_data	cdata;
197 
198 	a_flag = s_flag = f_flag = false;
199 
200 	while ((ch = getopt(argc, argv, "af:s:")) != -1) {
201 		switch (ch) {
202 		case 'a':
203 			a_flag = true;
204 			break;
205 		case 's':
206 			slot = strtol(optarg, &p, 0);
207 			if (p != NULL && *p != '\0') {
208 				fprintf(stderr,
209 				    "\"%s\" not valid slot.\n",
210 				    optarg);
211 				firmware_usage();
212 			} else if (slot == 0) {
213 				fprintf(stderr,
214 				    "0 is not a valid slot number. "
215 				    "Slot numbers start at 1.\n");
216 				firmware_usage();
217 			} else if (slot > 7) {
218 				fprintf(stderr,
219 				    "Slot number %s specified which is "
220 				    "greater than max allowed slot number of "
221 				    "7.\n", optarg);
222 				firmware_usage();
223 			}
224 			s_flag = true;
225 			break;
226 		case 'f':
227 			image = optarg;
228 			f_flag = true;
229 			break;
230 		}
231 	}
232 
233 	/* Check that a controller (and not a namespace) was specified. */
234 	if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL)
235 		firmware_usage();
236 
237 	if (!f_flag && !a_flag) {
238 		fprintf(stderr,
239 		    "Neither a replace ([-f path_to_firmware]) nor "
240 		    "activate ([-a]) firmware image action\n"
241 		    "was specified.\n");
242 		firmware_usage();
243 	}
244 
245 	if (!f_flag && a_flag && slot == 0) {
246 		fprintf(stderr,
247 		    "Slot number to activate not specified.\n");
248 		firmware_usage();
249 	}
250 
251 	controller = argv[optind];
252 	open_dev(controller, &fd, 1, 1);
253 	read_controller_data(fd, &cdata);
254 
255 	if (cdata.oacs.firmware == 0) {
256 		fprintf(stderr,
257 		    "Controller does not support firmware "
258 		    "activate/download.\n");
259 		exit(EX_IOERR);
260 	}
261 
262 	if (f_flag && slot == 1 && cdata.frmw.slot1_ro) {
263 		fprintf(stderr, "Slot %d is marked as read only.\n", slot);
264 		exit(EX_IOERR);
265 	}
266 
267 	if (slot > cdata.frmw.num_slots) {
268 		fprintf(stderr,
269 		    "Slot %d was specified but controller only "
270 		    "supports %d firmware slots.\n",
271 		    slot, cdata.frmw.num_slots);
272 		exit(EX_IOERR);
273 	}
274 
275 	if (!slot_has_valid_firmware(fd, slot)) {
276 		fprintf(stderr,
277 		    "Slot %d does not contain valid firmware.\n"
278 		    "Try 'nvmecontrol logpage -p 3 %s' to get a list "
279 		    "of available firmware images.\n",
280 		    slot, controller);
281 		exit(EX_IOERR);
282 	}
283 
284 	if (f_flag && a_flag)
285 		printf("You are about to download and activate "
286 		       "firmware image (%s) to controller %s.\n"
287 		       "This may damage your controller and/or "
288 		       "overwrite an existing firmware image.\n",
289 		       image, controller);
290 	else if (a_flag)
291 		printf("You are about to activate a new firmware "
292 		       "image on controller %s.\n"
293 		       "This may damage your controller.\n",
294 		       controller);
295 	else if (f_flag)
296 		printf("You are about to download firmware image "
297 		       "(%s) to controller %s.\n"
298 		       "This may damage your controller and/or "
299 		       "overwrite an existing firmware image.\n",
300 		       image, controller);
301 
302 	printf("Are you sure you want to continue? (yes/no) ");
303 	while (1) {
304 		fgets(prompt, sizeof(prompt), stdin);
305 		if (strncasecmp(prompt, "yes", 3) == 0)
306 			break;
307 		if (strncasecmp(prompt, "no", 2) == 0)
308 			exit(EX_OK);
309 		printf("Please answer \"yes\" or \"no\". ");
310 	}
311 
312 	if (f_flag) {
313 		read_image_file(image, &buf, &size);
314 		update_firmware(fd, buf, size);
315 		if (a_flag)
316 			activate_firmware(fd, slot,
317 			    NVME_AA_REPLACE_ACTIVATE);
318 		else
319 			activate_firmware(fd, slot,
320 			    NVME_AA_REPLACE_NO_ACTIVATE);
321 	} else {
322 		activate_firmware(fd, slot, NVME_AA_ACTIVATE);
323 	}
324 
325 	if (a_flag) {
326 		printf("New firmware image activated and will take "
327 		       "effect after next controller reset.\n"
328 		       "Controller reset can be initiated via "
329 		       "'nvmecontrol reset %s'\n",
330 		       controller);
331 	}
332 
333 	close(fd);
334 	exit(EX_OK);
335 }
336