1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013 EMC Corp.
5 * All rights reserved.
6 *
7 * Copyright (C) 2012-2013 Intel Corporation
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/ioccom.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39
40#include <ctype.h>
41#include <err.h>
42#include <fcntl.h>
43#include <inttypes.h>
44#include <stdbool.h>
45#include <stddef.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <sysexits.h>
50#include <unistd.h>
51
52#include "nvmecontrol.h"
53
54/* Tables for command line parsing */
55
56static cmd_fn_t firmware;
57
58#define NONE 0xffffffffu
59static struct options {
60	bool		activate;
61	uint32_t	slot;
62	const char	*fw_img;
63	const char	*dev;
64} opt = {
65	.activate = false,
66	.slot = NONE,
67	.fw_img = NULL,
68	.dev = NULL,
69};
70
71static const struct opts firmware_opts[] = {
72#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
73	OPT("activate", 'a', arg_none, opt, activate,
74	    "Attempt to activate firmware"),
75	OPT("slot", 's', arg_uint32, opt, slot,
76	    "Slot to activate and/or download firmware to"),
77	OPT("firmware", 'f', arg_path, opt, fw_img,
78	    "Firmware image to download"),
79	{ NULL, 0, arg_none, NULL, NULL }
80};
81#undef OPT
82
83static const struct args firmware_args[] = {
84	{ arg_string, &opt.dev, "controller-id|namespace-id" },
85	{ arg_none, NULL, NULL },
86};
87
88static struct cmd firmware_cmd = {
89	.name = "firmware",
90	.fn = firmware,
91	.descr = "Download firmware image to controller",
92	.ctx_size = sizeof(opt),
93	.opts = firmware_opts,
94	.args = firmware_args,
95};
96
97CMD_COMMAND(firmware_cmd);
98
99/* End of tables for command line parsing */
100
101static int
102slot_has_valid_firmware(int fd, int slot)
103{
104	struct nvme_firmware_page	fw;
105	int				has_fw = false;
106
107	read_logpage(fd, NVME_LOG_FIRMWARE_SLOT,
108	    NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw));
109
110	if (fw.revision[slot-1] != 0LLU)
111		has_fw = true;
112
113	return (has_fw);
114}
115
116static void
117read_image_file(const char *path, void **buf, int32_t *size)
118{
119	struct stat	sb;
120	int32_t		filesize;
121	int		fd;
122
123	*size = 0;
124	*buf = NULL;
125
126	if ((fd = open(path, O_RDONLY)) < 0)
127		err(EX_NOINPUT, "unable to open '%s'", path);
128	if (fstat(fd, &sb) < 0)
129		err(EX_NOINPUT, "unable to stat '%s'", path);
130
131	/*
132	 * The NVMe spec does not explicitly state a maximum firmware image
133	 *  size, although one can be inferred from the dword size limitation
134	 *  for the size and offset fields in the Firmware Image Download
135	 *  command.
136	 *
137	 * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the
138	 *  size and offsets are specified in terms of dwords (not bytes), but
139	 *  realistically INT32_MAX is sufficient here and simplifies matters
140	 *  a bit.
141	 */
142	if (sb.st_size > INT32_MAX)
143		errx(EX_USAGE, "size of file '%s' is too large (%jd bytes)",
144		    path, (intmax_t)sb.st_size);
145	filesize = (int32_t)sb.st_size;
146	if ((*buf = malloc(filesize)) == NULL)
147		errx(EX_OSERR, "unable to malloc %d bytes", filesize);
148	if ((*size = read(fd, *buf, filesize)) < 0)
149		err(EX_IOERR, "error reading '%s'", path);
150	/* XXX assuming no short reads */
151	if (*size != filesize)
152		errx(EX_IOERR,
153		    "error reading '%s' (read %d bytes, requested %d bytes)",
154		    path, *size, filesize);
155	close(fd);
156}
157
158static void
159update_firmware(int fd, uint8_t *payload, int32_t payload_size, uint8_t fwug)
160{
161	struct nvme_pt_command	pt;
162	uint64_t		max_xfer_size;
163	int32_t			off;
164	uint32_t		resid, size;
165	void			*chunk;
166
167	off = 0;
168	resid = payload_size;
169
170	if (ioctl(fd, NVME_GET_MAX_XFER_SIZE, &max_xfer_size) < 0)
171		err(EX_IOERR, "query max transfer size failed");
172	if (fwug != 0 && fwug != 0xFF)
173		max_xfer_size = MIN(max_xfer_size, (uint64_t)fwug << 12);
174
175	if ((chunk = aligned_alloc(PAGE_SIZE, max_xfer_size)) == NULL)
176		errx(EX_OSERR, "unable to malloc %zd bytes", (size_t)max_xfer_size);
177
178	while (resid > 0) {
179		size = (resid >= max_xfer_size) ?  max_xfer_size : resid;
180		memcpy(chunk, payload + off, size);
181
182		memset(&pt, 0, sizeof(pt));
183		pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
184		pt.cmd.cdw10 = htole32((size / sizeof(uint32_t)) - 1);
185		pt.cmd.cdw11 = htole32(off / sizeof(uint32_t));
186		pt.buf = chunk;
187		pt.len = size;
188		pt.is_read = 0;
189
190		if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
191			err(EX_IOERR, "firmware download request failed");
192
193		if (nvme_completion_is_error(&pt.cpl))
194			errx(EX_IOERR, "firmware download request returned error");
195
196		resid -= size;
197		off += size;
198	}
199	free(chunk);
200}
201
202static int
203activate_firmware(int fd, int slot, int activate_action)
204{
205	struct nvme_pt_command	pt;
206	uint16_t sct, sc;
207
208	memset(&pt, 0, sizeof(pt));
209	pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE;
210	pt.cmd.cdw10 = htole32((activate_action << 3) | slot);
211	pt.is_read = 0;
212
213	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
214		err(EX_IOERR, "firmware activate request failed");
215
216	sct = NVME_STATUS_GET_SCT(pt.cpl.status);
217	sc = NVME_STATUS_GET_SC(pt.cpl.status);
218
219	if (sct == NVME_SCT_COMMAND_SPECIFIC &&
220	    sc == NVME_SC_FIRMWARE_REQUIRES_RESET)
221		return 1;
222
223	if (nvme_completion_is_error(&pt.cpl))
224		errx(EX_IOERR, "firmware activate request returned error");
225
226	return 0;
227}
228
229static void
230firmware(const struct cmd *f, int argc, char *argv[])
231{
232	int				fd = -1;
233	int				activate_action, reboot_required;
234	char				prompt[64];
235	void				*buf = NULL;
236	char				*path;
237	int32_t				size = 0, nsid;
238	uint16_t			oacs_fw;
239	uint8_t				fw_slot1_ro, fw_num_slots;
240	struct nvme_controller_data	cdata;
241
242	if (arg_parse(argc, argv, f))
243		return;
244
245	if (opt.slot == 0) {
246		fprintf(stderr,
247		    "0 is not a valid slot number. "
248		    "Slot numbers start at 1.\n");
249		arg_help(argc, argv, f);
250	} else if (opt.slot > 7 && opt.slot != NONE) {
251		fprintf(stderr,
252		    "Slot number %s specified which is "
253		    "greater than max allowed slot number of "
254		    "7.\n", optarg);
255		arg_help(argc, argv, f);
256	}
257
258	if (!opt.activate && opt.fw_img == NULL) {
259		fprintf(stderr,
260		    "Neither a replace ([-f path_to_firmware]) nor "
261		    "activate ([-a]) firmware image action\n"
262		    "was specified.\n");
263		arg_help(argc, argv, f);
264	}
265
266	if (opt.activate && opt.fw_img == NULL && opt.slot == 0) {
267		fprintf(stderr,
268		    "Slot number to activate not specified.\n");
269		arg_help(argc, argv, f);
270	}
271
272	open_dev(opt.dev, &fd, 1, 1);
273	get_nsid(fd, &path, &nsid);
274	if (nsid != 0) {
275		close(fd);
276		open_dev(path, &fd, 1, 1);
277	}
278	free(path);
279
280	if (read_controller_data(fd, &cdata))
281		errx(EX_IOERR, "Identify request failed");
282
283	oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
284		NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
285
286	if (oacs_fw == 0)
287		errx(EX_UNAVAILABLE,
288		    "controller does not support firmware activate/download");
289
290	fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) &
291		NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK;
292
293	if (opt.fw_img && opt.slot == 1 && fw_slot1_ro)
294		errx(EX_UNAVAILABLE, "slot %d is marked as read only", opt.slot);
295
296	fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
297		NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
298
299	if (opt.slot > fw_num_slots)
300		errx(EX_UNAVAILABLE,
301		    "slot %d specified but controller only supports %d slots",
302		    opt.slot, fw_num_slots);
303
304	if (opt.activate && opt.fw_img == NULL &&
305	    !slot_has_valid_firmware(fd, opt.slot))
306		errx(EX_UNAVAILABLE,
307		    "slot %d does not contain valid firmware,\n"
308		    "try 'nvmecontrol logpage -p 3 %s' to get a list "
309		    "of available images\n",
310		    opt.slot, opt.dev);
311
312	if (opt.fw_img)
313		read_image_file(opt.fw_img, &buf, &size);
314
315	if (opt.fw_img != NULL&& opt.activate)
316		printf("You are about to download and activate "
317		       "firmware image (%s) to controller %s.\n"
318		       "This may damage your controller and/or "
319		       "overwrite an existing firmware image.\n",
320		       opt.fw_img, opt.dev);
321	else if (opt.activate)
322		printf("You are about to activate a new firmware "
323		       "image on controller %s.\n"
324		       "This may damage your controller.\n",
325		       opt.dev);
326	else if (opt.fw_img != NULL)
327		printf("You are about to download firmware image "
328		       "(%s) to controller %s.\n"
329		       "This may damage your controller and/or "
330		       "overwrite an existing firmware image.\n",
331		       opt.fw_img, opt.dev);
332
333	printf("Are you sure you want to continue? (yes/no) ");
334	while (1) {
335		fgets(prompt, sizeof(prompt), stdin);
336		if (strncasecmp(prompt, "yes", 3) == 0)
337			break;
338		if (strncasecmp(prompt, "no", 2) == 0)
339			exit(EX_DATAERR);
340		printf("Please answer \"yes\" or \"no\". ");
341	}
342
343	if (opt.fw_img != NULL) {
344		update_firmware(fd, buf, size, cdata.fwug);
345		if (opt.activate)
346			activate_action = NVME_AA_REPLACE_ACTIVATE;
347		else
348			activate_action = NVME_AA_REPLACE_NO_ACTIVATE;
349	} else {
350		activate_action = NVME_AA_ACTIVATE;
351	}
352
353	reboot_required = activate_firmware(fd, opt.slot, activate_action);
354
355	if (opt.activate) {
356		if (reboot_required) {
357			printf("New firmware image activated but requires "
358			       "conventional reset (i.e. reboot) to "
359			       "complete activation.\n");
360		} else {
361			printf("New firmware image activated and will take "
362			       "effect after next controller reset.\n"
363			       "Controller reset can be initiated via "
364			       "'nvmecontrol reset %s'\n",
365			       opt.dev);
366		}
367	}
368
369	close(fd);
370	exit(0);
371}
372