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 <err.h> 40 #include <fcntl.h> 41 #include <inttypes.h> 42 #include <stdbool.h> 43 #include <stddef.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.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, int32_t *size) 68 { 69 struct stat sb; 70 int32_t filesize; 71 int fd; 72 73 *size = 0; 74 *buf = NULL; 75 76 if ((fd = open(path, O_RDONLY)) < 0) 77 err(1, "unable to open '%s'", path); 78 if (fstat(fd, &sb) < 0) 79 err(1, "unable to stat '%s'", path); 80 81 /* 82 * The NVMe spec does not explicitly state a maximum firmware image 83 * size, although one can be inferred from the dword size limitation 84 * for the size and offset fields in the Firmware Image Download 85 * command. 86 * 87 * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the 88 * size and offsets are specified in terms of dwords (not bytes), but 89 * realistically INT32_MAX is sufficient here and simplifies matters 90 * a bit. 91 */ 92 if (sb.st_size > INT32_MAX) 93 errx(1, "size of file '%s' is too large (%jd bytes)", 94 path, (intmax_t)sb.st_size); 95 filesize = (int32_t)sb.st_size; 96 if ((*buf = malloc(filesize)) == NULL) 97 errx(1, "unable to malloc %d bytes", filesize); 98 if ((*size = read(fd, *buf, filesize)) < 0) 99 err(1, "error reading '%s'", path); 100 /* XXX assuming no short reads */ 101 if (*size != filesize) 102 errx(1, 103 "error reading '%s' (read %d bytes, requested %d bytes)", 104 path, *size, filesize); 105 } 106 107 static void 108 update_firmware(int fd, uint8_t *payload, int32_t payload_size) 109 { 110 struct nvme_pt_command pt; 111 int32_t off, resid, size; 112 void *chunk; 113 114 off = 0; 115 resid = payload_size; 116 117 if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL) 118 errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); 119 120 while (resid > 0) { 121 size = (resid >= NVME_MAX_XFER_SIZE) ? 122 NVME_MAX_XFER_SIZE : resid; 123 memcpy(chunk, payload + off, size); 124 125 memset(&pt, 0, sizeof(pt)); 126 pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; 127 pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1; 128 pt.cmd.cdw11 = (off / sizeof(uint32_t)); 129 pt.buf = chunk; 130 pt.len = size; 131 pt.is_read = 0; 132 133 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 134 err(1, "firmware download request failed"); 135 136 if (nvme_completion_is_error(&pt.cpl)) 137 errx(1, "firmware download request returned error"); 138 139 resid -= size; 140 off += size; 141 } 142 } 143 144 static void 145 activate_firmware(int fd, int slot, int activate_action) 146 { 147 struct nvme_pt_command pt; 148 149 memset(&pt, 0, sizeof(pt)); 150 pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; 151 pt.cmd.cdw10 = (activate_action << 3) | slot; 152 pt.is_read = 0; 153 154 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 155 err(1, "firmware activate request failed"); 156 157 if (nvme_completion_is_error(&pt.cpl)) 158 errx(1, "firmware activate request returned error"); 159 } 160 161 static void 162 firmware_usage(void) 163 { 164 fprintf(stderr, "usage:\n"); 165 fprintf(stderr, FIRMWARE_USAGE); 166 exit(1); 167 } 168 169 void 170 firmware(int argc, char *argv[]) 171 { 172 int fd = -1, slot = 0; 173 int a_flag, s_flag, f_flag; 174 char ch, *p, *image = NULL; 175 char *controller = NULL, prompt[64]; 176 void *buf = NULL; 177 int32_t size = 0; 178 struct nvme_controller_data cdata; 179 180 a_flag = s_flag = f_flag = false; 181 182 while ((ch = getopt(argc, argv, "af:s:")) != -1) { 183 switch (ch) { 184 case 'a': 185 a_flag = true; 186 break; 187 case 's': 188 slot = strtol(optarg, &p, 0); 189 if (p != NULL && *p != '\0') { 190 fprintf(stderr, 191 "\"%s\" not valid slot.\n", 192 optarg); 193 firmware_usage(); 194 } else if (slot == 0) { 195 fprintf(stderr, 196 "0 is not a valid slot number. " 197 "Slot numbers start at 1.\n"); 198 firmware_usage(); 199 } else if (slot > 7) { 200 fprintf(stderr, 201 "Slot number %s specified which is " 202 "greater than max allowed slot number of " 203 "7.\n", optarg); 204 firmware_usage(); 205 } 206 s_flag = true; 207 break; 208 case 'f': 209 image = optarg; 210 f_flag = true; 211 break; 212 } 213 } 214 215 /* Check that a controller (and not a namespace) was specified. */ 216 if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL) 217 firmware_usage(); 218 219 if (!f_flag && !a_flag) { 220 fprintf(stderr, 221 "Neither a replace ([-f path_to_firmware]) nor " 222 "activate ([-a]) firmware image action\n" 223 "was specified.\n"); 224 firmware_usage(); 225 } 226 227 if (!f_flag && a_flag && slot == 0) { 228 fprintf(stderr, 229 "Slot number to activate not specified.\n"); 230 firmware_usage(); 231 } 232 233 controller = argv[optind]; 234 open_dev(controller, &fd, 1, 1); 235 read_controller_data(fd, &cdata); 236 237 if (cdata.oacs.firmware == 0) 238 errx(1, 239 "controller does not support firmware activate/download"); 240 241 if (f_flag && slot == 1 && cdata.frmw.slot1_ro) 242 errx(1, "slot %d is marked as read only", slot); 243 244 if (slot > cdata.frmw.num_slots) 245 errx(1, 246 "slot %d specified but controller only supports %d slots", 247 slot, cdata.frmw.num_slots); 248 249 if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot)) 250 errx(1, 251 "slot %d does not contain valid firmware,\n" 252 "try 'nvmecontrol logpage -p 3 %s' to get a list " 253 "of available images\n", 254 slot, controller); 255 256 if (f_flag) 257 read_image_file(image, &buf, &size); 258 259 if (f_flag && a_flag) 260 printf("You are about to download and activate " 261 "firmware image (%s) to controller %s.\n" 262 "This may damage your controller and/or " 263 "overwrite an existing firmware image.\n", 264 image, controller); 265 else if (a_flag) 266 printf("You are about to activate a new firmware " 267 "image on controller %s.\n" 268 "This may damage your controller.\n", 269 controller); 270 else if (f_flag) 271 printf("You are about to download firmware image " 272 "(%s) to controller %s.\n" 273 "This may damage your controller and/or " 274 "overwrite an existing firmware image.\n", 275 image, controller); 276 277 printf("Are you sure you want to continue? (yes/no) "); 278 while (1) { 279 fgets(prompt, sizeof(prompt), stdin); 280 if (strncasecmp(prompt, "yes", 3) == 0) 281 break; 282 if (strncasecmp(prompt, "no", 2) == 0) 283 exit(1); 284 printf("Please answer \"yes\" or \"no\". "); 285 } 286 287 if (f_flag) { 288 update_firmware(fd, buf, size); 289 if (a_flag) 290 activate_firmware(fd, slot, 291 NVME_AA_REPLACE_ACTIVATE); 292 else 293 activate_firmware(fd, slot, 294 NVME_AA_REPLACE_NO_ACTIVATE); 295 } else { 296 activate_firmware(fd, slot, NVME_AA_ACTIVATE); 297 } 298 299 if (a_flag) { 300 printf("New firmware image activated and will take " 301 "effect after next controller reset.\n" 302 "Controller reset can be initiated via " 303 "'nvmecontrol reset %s'\n", 304 controller); 305 } 306 307 close(fd); 308 exit(0); 309 } 310