firmware.c revision 253393
1187767Sluigi/*-
2187767Sluigi * Copyright (c) 2013 EMC Corp.
3187767Sluigi * All rights reserved.
4187767Sluigi *
5187767Sluigi * Copyright (C) 2012-2013 Intel Corporation
6187767Sluigi * All rights reserved.
7187767Sluigi *
8187767Sluigi * Redistribution and use in source and binary forms, with or without
9187767Sluigi * modification, are permitted provided that the following conditions
10187767Sluigi * are met:
11187767Sluigi * 1. Redistributions of source code must retain the above copyright
12187767Sluigi *    notice, this list of conditions and the following disclaimer.
13187767Sluigi * 2. Redistributions in binary form must reproduce the above copyright
14187767Sluigi *    notice, this list of conditions and the following disclaimer in the
15187767Sluigi *    documentation and/or other materials provided with the distribution.
16187767Sluigi *
17187767Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18187767Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19187767Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20187767Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21187767Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22187767Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23187767Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24187767Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25187767Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26187767Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27187767Sluigi * SUCH DAMAGE.
28187767Sluigi */
29187767Sluigi
30187767Sluigi#include <sys/cdefs.h>
31187767Sluigi__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 253393 2013-07-16 15:45:37Z jimharris $");
32187767Sluigi
33187767Sluigi#include <sys/param.h>
34187767Sluigi#include <sys/ioccom.h>
35187767Sluigi#include <sys/stat.h>
36187767Sluigi#include <sys/types.h>
37187767Sluigi
38204591Sluigi#include <ctype.h>
39187767Sluigi#include <err.h>
40187767Sluigi#include <fcntl.h>
41187767Sluigi#include <inttypes.h>
42187767Sluigi#include <stdbool.h>
43187767Sluigi#include <stddef.h>
44187767Sluigi#include <stdio.h>
45187767Sluigi#include <stdlib.h>
46187767Sluigi#include <string.h>
47187767Sluigi#include <unistd.h>
48187767Sluigi
49346205Sae#include "nvmecontrol.h"
50346205Sae
51187767Sluigistatic int
52187767Sluigislot_has_valid_firmware(int fd, int slot)
53187767Sluigi{
54187767Sluigi	struct nvme_firmware_page	fw;
55187767Sluigi	int				has_fw = false;
56187767Sluigi
57187767Sluigi	read_logpage(fd, NVME_LOG_FIRMWARE_SLOT,
58187767Sluigi	    NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw));
59332400Sae
60332400Sae	if (fw.revision[slot-1] != 0LLU)
61332400Sae		has_fw = true;
62332400Sae
63332400Sae	return (has_fw);
64332400Sae}
65187767Sluigi
66187767Sluigistatic void
67187767Sluigiread_image_file(char *path, void **buf, int32_t *size)
68187767Sluigi{
69187767Sluigi	struct stat	sb;
70187767Sluigi	int32_t		filesize;
71187767Sluigi	int		fd;
72187767Sluigi
73187767Sluigi	*size = 0;
74187767Sluigi	*buf = NULL;
75187767Sluigi
76187767Sluigi	if ((fd = open(path, O_RDONLY)) < 0)
77187767Sluigi		err(1, "unable to open '%s'", path);
78187767Sluigi	if (fstat(fd, &sb) < 0)
79187767Sluigi		err(1, "unable to stat '%s'", path);
80270424Smelifaro
81270424Smelifaro	/*
82187769Sluigi	 * The NVMe spec does not explicitly state a maximum firmware image
83187769Sluigi	 *  size, although one can be inferred from the dword size limitation
84187769Sluigi	 *  for the size and offset fields in the Firmware Image Download
85187769Sluigi	 *  command.
86187769Sluigi	 *
87187769Sluigi	 * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the
88187769Sluigi	 *  size and offsets are specified in terms of dwords (not bytes), but
89187769Sluigi	 *  realistically INT32_MAX is sufficient here and simplifies matters
90332229Stuexen	 *  a bit.
91332229Stuexen	 */
92187769Sluigi	if (sb.st_size > INT32_MAX)
93187769Sluigi		errx(1, "size of file '%s' is too large (%jd bytes)",
94298016Sae		    path, (intmax_t)sb.st_size);
95187769Sluigi	filesize = (int32_t)sb.st_size;
96204591Sluigi	if ((*buf = malloc(filesize)) == NULL)
97187769Sluigi		errx(1, "unable to malloc %d bytes", filesize);
98204591Sluigi	if ((*size = read(fd, *buf, filesize)) < 0)
99204591Sluigi		err(1, "error reading '%s'", path);
100187769Sluigi	/* XXX assuming no short reads */
101187769Sluigi	if (*size != filesize)
102187769Sluigi		errx(1,
103187769Sluigi		    "error reading '%s' (read %d bytes, requested %d bytes)",
104187769Sluigi		    path, *size, filesize);
105187769Sluigi}
106187769Sluigi
107187769Sluigistatic void
108187769Sluigiupdate_firmware(int fd, uint8_t *payload, int32_t payload_size)
109187769Sluigi{
110187769Sluigi	struct nvme_pt_command	pt;
111187769Sluigi	int32_t			off, resid, size;
112190633Spiso	void			*chunk;
113223666Sae
114223666Sae	off = 0;
115187769Sluigi	resid = payload_size;
116187769Sluigi
117187769Sluigi	if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL)
118187769Sluigi		errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE);
119187769Sluigi
120187769Sluigi	while (resid > 0) {
121187769Sluigi		size = (resid >= NVME_MAX_XFER_SIZE) ?
122187769Sluigi		    NVME_MAX_XFER_SIZE : resid;
123187769Sluigi		memcpy(chunk, payload + off, size);
124187769Sluigi
125187769Sluigi		memset(&pt, 0, sizeof(pt));
126187769Sluigi		pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
127337461Sae		pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1;
128187769Sluigi		pt.cmd.cdw11 = (off / sizeof(uint32_t));
129337461Sae		pt.buf = chunk;
130187769Sluigi		pt.len = size;
131187769Sluigi		pt.is_read = 0;
132187769Sluigi
133187769Sluigi		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
134187769Sluigi			err(1, "firmware download request failed");
135187769Sluigi
136187769Sluigi		if (nvme_completion_is_error(&pt.cpl))
137187769Sluigi			errx(1, "firmware download request returned error");
138187769Sluigi
139187769Sluigi		resid -= size;
140187769Sluigi		off += size;
141187769Sluigi	}
142187769Sluigi}
143205169Sluigi
144187769Sluigistatic void
145187769Sluigiactivate_firmware(int fd, int slot, int activate_action)
146187769Sluigi{
147187769Sluigi	struct nvme_pt_command	pt;
148187769Sluigi
149187769Sluigi	memset(&pt, 0, sizeof(pt));
150187769Sluigi	pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE;
151187769Sluigi	pt.cmd.cdw10 = (activate_action << 3) | slot;
152187769Sluigi	pt.is_read = 0;
153187769Sluigi
154187769Sluigi	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
155187769Sluigi		err(1, "firmware activate request failed");
156187769Sluigi
157187769Sluigi	if (nvme_completion_is_error(&pt.cpl))
158187769Sluigi		errx(1, "firmware activate request returned error");
159187769Sluigi}
160187769Sluigi
161187769Sluigistatic void
162187769Sluigifirmware_usage(void)
163187769Sluigi{
164187769Sluigi	fprintf(stderr, "usage:\n");
165187769Sluigi	fprintf(stderr, FIRMWARE_USAGE);
166187769Sluigi	exit(1);
167187769Sluigi}
168187769Sluigi
169187769Sluigivoid
170187769Sluigifirmware(int argc, char *argv[])
171187769Sluigi{
172187769Sluigi	int				fd = -1, slot = 0;
173204591Sluigi	int				a_flag, s_flag, f_flag;
174204591Sluigi	char				ch, *p, *image = NULL;
175187769Sluigi	char				*controller = NULL, prompt[64];
176187769Sluigi	void				*buf = NULL;
177204591Sluigi	int32_t				size = 0;
178194930Soleg	struct nvme_controller_data	cdata;
179187769Sluigi
180187769Sluigi	a_flag = s_flag = f_flag = false;
181266941Shiren
182187769Sluigi	while ((ch = getopt(argc, argv, "af:s:")) != -1) {
183187769Sluigi		switch (ch) {
184300779Struckman		case 'a':
185300779Struckman			a_flag = true;
186300779Struckman			break;
187300779Struckman		case 's':
188300779Struckman			slot = strtol(optarg, &p, 0);
189300779Struckman			if (p != NULL && *p != '\0') {
190300779Struckman				fprintf(stderr,
191300779Struckman				    "\"%s\" not valid slot.\n",
192300779Struckman				    optarg);
193300779Struckman				firmware_usage();
194300779Struckman			} else if (slot == 0) {
195300779Struckman				fprintf(stderr,
196300779Struckman				    "0 is not a valid slot number. "
197300779Struckman				    "Slot numbers start at 1.\n");
198300779Struckman				firmware_usage();
199300779Struckman			} else if (slot > 7) {
200300779Struckman				fprintf(stderr,
201300779Struckman				    "Slot number %s specified which is "
202300779Struckman				    "greater than max allowed slot number of "
203300779Struckman				    "7.\n", optarg);
204300779Struckman				firmware_usage();
205300779Struckman			}
206300779Struckman			s_flag = true;
207300779Struckman			break;
208300779Struckman		case 'f':
209204591Sluigi			image = optarg;
210187769Sluigi			f_flag = true;
211204591Sluigi			break;
212204591Sluigi		}
213204591Sluigi	}
214204591Sluigi
215204591Sluigi	/* Check that a controller (and not a namespace) was specified. */
216187769Sluigi	if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL)
217187769Sluigi		firmware_usage();
218332210Stuexen
219332210Stuexen	if (!f_flag && !a_flag) {
220332210Stuexen		fprintf(stderr,
221332210Stuexen		    "Neither a replace ([-f path_to_firmware]) nor "
222223080Sae		    "activate ([-a]) firmware image action\n"
223332210Stuexen		    "was specified.\n");
224332210Stuexen		firmware_usage();
225332210Stuexen	}
226187769Sluigi
227187769Sluigi	if (!f_flag && a_flag && slot == 0) {
228220804Sglebius		fprintf(stderr,
229187769Sluigi		    "Slot number to activate not specified.\n");
230187769Sluigi		firmware_usage();
231187769Sluigi	}
232187769Sluigi
233187769Sluigi	controller = argv[optind];
234187769Sluigi	open_dev(controller, &fd, 1, 1);
235187769Sluigi	read_controller_data(fd, &cdata);
236187769Sluigi
237187769Sluigi	if (cdata.oacs.firmware == 0)
238187769Sluigi		errx(1,
239187769Sluigi		    "controller does not support firmware activate/download");
240187769Sluigi
241187769Sluigi	if (f_flag && slot == 1 && cdata.frmw.slot1_ro)
242187769Sluigi		errx(1, "slot %d is marked as read only", slot);
243200567Sluigi
244215179Sluigi	if (slot > cdata.frmw.num_slots)
245248552Smelifaro		errx(1,
246272840Smelifaro		    "slot %d specified but controller only supports %d slots",
247272840Smelifaro		    slot, cdata.frmw.num_slots);
248272840Smelifaro
249272840Smelifaro	if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot))
250272840Smelifaro		errx(1,
251272840Smelifaro		    "slot %d does not contain valid firmware,\n"
252272840Smelifaro		    "try 'nvmecontrol logpage -p 3 %s' to get a list "
253272840Smelifaro		    "of available images\n",
254272840Smelifaro		    slot, controller);
255272840Smelifaro
256272840Smelifaro	if (f_flag)
257272840Smelifaro		read_image_file(image, &buf, &size);
258272840Smelifaro
259272840Smelifaro	if (f_flag && a_flag)
260272840Smelifaro		printf("You are about to download and activate "
261272840Smelifaro		       "firmware image (%s) to controller %s.\n"
262272840Smelifaro		       "This may damage your controller and/or "
263272840Smelifaro		       "overwrite an existing firmware image.\n",
264272840Smelifaro		       image, controller);
265272840Smelifaro	else if (a_flag)
266290330Sae		printf("You are about to activate a new firmware "
267316446Sae		       "image on controller %s.\n"
268316446Sae		       "This may damage your controller.\n",
269316446Sae		       controller);
270316446Sae	else if (f_flag)
271316444Sae		printf("You are about to download firmware image "
272316446Sae		       "(%s) to controller %s.\n"
273316446Sae		       "This may damage your controller and/or "
274316446Sae		       "overwrite an existing firmware image.\n",
275316446Sae		       image, controller);
276316446Sae
277316446Sae	printf("Are you sure you want to continue? (yes/no) ");
278316446Sae	while (1) {
279316446Sae		fgets(prompt, sizeof(prompt), stdin);
280316446Sae		if (strncasecmp(prompt, "yes", 3) == 0)
281316446Sae			break;
282316446Sae		if (strncasecmp(prompt, "no", 2) == 0)
283316446Sae			exit(1);
284316446Sae		printf("Please answer \"yes\" or \"no\". ");
285316446Sae	}
286316446Sae
287316446Sae	if (f_flag) {
288316446Sae		update_firmware(fd, buf, size);
289316446Sae		if (a_flag)
290316446Sae			activate_firmware(fd, slot,
291346210Sae			    NVME_AA_REPLACE_ACTIVATE);
292346210Sae		else
293316444Sae			activate_firmware(fd, slot,
294316444Sae			    NVME_AA_REPLACE_NO_ACTIVATE);
295316444Sae	} else {
296316444Sae		activate_firmware(fd, slot, NVME_AA_ACTIVATE);
297316444Sae	}
298316444Sae
299317045Sae	if (a_flag) {
300317045Sae		printf("New firmware image activated and will take "
301337461Sae		       "effect after next controller reset.\n"
302337461Sae		       "Controller reset can be initiated via "
303187769Sluigi		       "'nvmecontrol reset %s'\n",
304272840Smelifaro		       controller);
305187767Sluigi	}
306187767Sluigi
307187767Sluigi	close(fd);
308187767Sluigi	exit(0);
309204591Sluigi}
310204591Sluigi