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