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 int 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 (pt.cpl.status.sct == NVME_SCT_COMMAND_SPECIFIC && 158 pt.cpl.status.sc == NVME_SC_FIRMWARE_REQUIRES_RESET) 159 return 1; 160 161 if (nvme_completion_is_error(&pt.cpl)) 162 errx(1, "firmware activate request returned error"); 163 164 return 0; 165 } 166 167 static void 168 firmware_usage(void) 169 { 170 fprintf(stderr, "usage:\n"); 171 fprintf(stderr, FIRMWARE_USAGE); 172 exit(1); 173 } 174 175 void 176 firmware(int argc, char *argv[]) 177 { 178 int fd = -1, slot = 0; 179 int a_flag, s_flag, f_flag; 180 int activate_action, reboot_required; 181 char ch, *p, *image = NULL; 182 char *controller = NULL, prompt[64]; 183 void *buf = NULL; 184 int32_t size = 0; 185 struct nvme_controller_data cdata; 186 187 a_flag = s_flag = f_flag = false; 188 189 while ((ch = getopt(argc, argv, "af:s:")) != -1) { 190 switch (ch) { 191 case 'a': 192 a_flag = true; 193 break; 194 case 's': 195 slot = strtol(optarg, &p, 0); 196 if (p != NULL && *p != '\0') { 197 fprintf(stderr, 198 "\"%s\" not valid slot.\n", 199 optarg); 200 firmware_usage(); 201 } else if (slot == 0) { 202 fprintf(stderr, 203 "0 is not a valid slot number. " 204 "Slot numbers start at 1.\n"); 205 firmware_usage(); 206 } else if (slot > 7) { 207 fprintf(stderr, 208 "Slot number %s specified which is " 209 "greater than max allowed slot number of " 210 "7.\n", optarg); 211 firmware_usage(); 212 } 213 s_flag = true; 214 break; 215 case 'f': 216 image = optarg; 217 f_flag = true; 218 break; 219 } 220 } 221 222 /* Check that a controller (and not a namespace) was specified. */ 223 if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL) 224 firmware_usage(); 225 226 if (!f_flag && !a_flag) { 227 fprintf(stderr, 228 "Neither a replace ([-f path_to_firmware]) nor " 229 "activate ([-a]) firmware image action\n" 230 "was specified.\n"); 231 firmware_usage(); 232 } 233 234 if (!f_flag && a_flag && slot == 0) { 235 fprintf(stderr, 236 "Slot number to activate not specified.\n"); 237 firmware_usage(); 238 } 239 240 controller = argv[optind]; 241 open_dev(controller, &fd, 1, 1); 242 read_controller_data(fd, &cdata); 243 244 if (cdata.oacs.firmware == 0) 245 errx(1, 246 "controller does not support firmware activate/download"); 247 248 if (f_flag && slot == 1 && cdata.frmw.slot1_ro) 249 errx(1, "slot %d is marked as read only", slot); 250 251 if (slot > cdata.frmw.num_slots) 252 errx(1, 253 "slot %d specified but controller only supports %d slots", 254 slot, cdata.frmw.num_slots); 255 256 if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot)) 257 errx(1, 258 "slot %d does not contain valid firmware,\n" 259 "try 'nvmecontrol logpage -p 3 %s' to get a list " 260 "of available images\n", 261 slot, controller); 262 263 if (f_flag) 264 read_image_file(image, &buf, &size); 265 266 if (f_flag && a_flag) 267 printf("You are about to download and activate " 268 "firmware image (%s) to controller %s.\n" 269 "This may damage your controller and/or " 270 "overwrite an existing firmware image.\n", 271 image, controller); 272 else if (a_flag) 273 printf("You are about to activate a new firmware " 274 "image on controller %s.\n" 275 "This may damage your controller.\n", 276 controller); 277 else if (f_flag) 278 printf("You are about to download firmware image " 279 "(%s) to controller %s.\n" 280 "This may damage your controller and/or " 281 "overwrite an existing firmware image.\n", 282 image, controller); 283 284 printf("Are you sure you want to continue? (yes/no) "); 285 while (1) { 286 fgets(prompt, sizeof(prompt), stdin); 287 if (strncasecmp(prompt, "yes", 3) == 0) 288 break; 289 if (strncasecmp(prompt, "no", 2) == 0) 290 exit(1); 291 printf("Please answer \"yes\" or \"no\". "); 292 } 293 294 if (f_flag) { 295 update_firmware(fd, buf, size); 296 if (a_flag) 297 activate_action = NVME_AA_REPLACE_ACTIVATE; 298 else 299 activate_action = NVME_AA_REPLACE_NO_ACTIVATE; 300 } else { 301 activate_action = NVME_AA_ACTIVATE; 302 } 303 304 reboot_required = activate_firmware(fd, slot, activate_action); 305 306 if (a_flag) { 307 if (reboot_required) { 308 printf("New firmware image activated but requires " 309 "conventional reset (i.e. reboot) to " 310 "complete activation.\n"); 311 } else { 312 printf("New firmware image activated and will take " 313 "effect after next controller reset.\n" 314 "Controller reset can be initiated via " 315 "'nvmecontrol reset %s'\n", 316 controller); 317 } 318 } 319 320 close(fd); 321 exit(0); 322 } 323