]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sbin/nvmecontrol/firmware.c
MFC r252278:
[FreeBSD/stable/9.git] / sbin / nvmecontrol / firmware.c
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 }