1/* $NetBSD: firmware.c,v 1.6 2023/07/05 10:58:46 riastradh Exp $ */ 2 3/*- 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (c) 2013 EMC Corp. 7 * All rights reserved. 8 * 9 * Copyright (C) 2012-2013 Intel Corporation 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> 35#ifndef lint 36__RCSID("$NetBSD: firmware.c,v 1.6 2023/07/05 10:58:46 riastradh Exp $"); 37#if 0 38__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 329824 2018-02-22 13:32:31Z wma $"); 39#endif 40#endif 41 42#include <sys/param.h> 43#include <sys/ioccom.h> 44#include <sys/stat.h> 45#include <sys/types.h> 46 47#include <ctype.h> 48#include <err.h> 49#include <fcntl.h> 50#include <inttypes.h> 51#include <stdbool.h> 52#include <stddef.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <unistd.h> 57 58#include "nvmectl.h" 59 60#ifdef FIRMWARE_USAGE 61static int 62slot_has_valid_firmware(int fd, int slot) 63{ 64 struct nvme_firmware_page fw; 65 int has_fw = false; 66 67 read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, 0xffffffff, &fw, sizeof(fw)); 68 69 if (fw.revision[slot-1] != 0LLU) 70 has_fw = true; 71 72 return (has_fw); 73} 74 75static void 76read_image_file(char *path, void **buf, int32_t *size) 77{ 78 struct stat sb; 79 int32_t filesize; 80 int fd; 81 82 *size = 0; 83 *buf = NULL; 84 85 if ((fd = open(path, O_RDONLY)) < 0) 86 err(1, "unable to open '%s'", path); 87 if (fstat(fd, &sb) < 0) 88 err(1, "unable to stat '%s'", path); 89 90 /* 91 * The NVMe spec does not explicitly state a maximum firmware image 92 * size, although one can be inferred from the dword size limitation 93 * for the size and offset fields in the Firmware Image Download 94 * command. 95 * 96 * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the 97 * size and offsets are specified in terms of dwords (not bytes), but 98 * realistically INT32_MAX is sufficient here and simplifies matters 99 * a bit. 100 */ 101 if (sb.st_size > INT32_MAX) 102 errx(1, "size of file '%s' is too large (%jd bytes)", 103 path, (intmax_t)sb.st_size); 104 filesize = (int32_t)sb.st_size; 105 if ((*buf = malloc(filesize)) == NULL) 106 errx(1, "unable to malloc %d bytes", filesize); 107 if ((*size = read(fd, *buf, filesize)) < 0) 108 err(1, "error reading '%s'", path); 109 /* XXX assuming no short reads */ 110 if (*size != filesize) 111 errx(1, 112 "error reading '%s' (read %d bytes, requested %d bytes)", 113 path, *size, filesize); 114} 115 116static void 117update_firmware(int fd, uint8_t *payload, int32_t payload_size) 118{ 119 struct nvme_pt_command pt; 120 int32_t off, resid, size; 121 void *chunk; 122 123 off = 0; 124 resid = payload_size; 125 126 if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL) 127 errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); 128 129 while (resid > 0) { 130 size = (resid >= NVME_MAX_XFER_SIZE) ? 131 NVME_MAX_XFER_SIZE : resid; 132 memcpy(chunk, payload + off, size); 133 134 memset(&pt, 0, sizeof(pt)); 135 pt.cmd.opcode = NVM_ADMIN_FW_DOWNLOAD; 136 pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1; 137 pt.cmd.cdw11 = (off / sizeof(uint32_t)); 138 pt.buf = chunk; 139 pt.len = size; 140 pt.is_read = 0; 141 142 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 143 err(1, "firmware download request failed"); 144 145 if (nvme_completion_is_error(&pt.cpl)) 146 errx(1, "firmware download request returned error"); 147 148 resid -= size; 149 off += size; 150 } 151} 152 153static int 154activate_firmware(int fd, int slot, int commit_action) 155{ 156 struct nvme_pt_command pt; 157 158 memset(&pt, 0, sizeof(pt)); 159 pt.cmd.opcode = NVM_ADMIN_FW_COMMIT; 160 pt.cmd.cdw10 = (commit_action << 3) | slot; 161 pt.is_read = 0; 162 163 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 164 err(1, "firmware activate request failed"); 165 166 if (NVME_CQE_SCT(pt.cpl.flags) == NVME_CQE_SCT_COMMAND && 167 NVME_CQE_SC(pt.cpl.flags) == NVME_CQE_SC_FW_REQ_RESET) 168 return 1; 169 170 if (nvme_completion_is_error(&pt.cpl)) 171 errx(1, "firmware activate request returned error"); 172 173 return 0; 174} 175 176static void 177firmware_usage(void) 178{ 179 fprintf(stderr, "usage:\n"); 180 fprintf(stderr, FIRMWARE_USAGE); 181 exit(1); 182} 183 184void 185firmware(int argc, char *argv[]) 186{ 187 u_int slot = 0; 188 int fd = -1; 189 int a_flag, s_flag, f_flag; 190 int commit_action, reboot_required; 191 int ch; 192 char *p, *image = NULL; 193 char *controller = NULL, prompt[64]; 194 void *buf = NULL; 195 int32_t size = 0; 196 struct nvm_identify_controller 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 & NVME_ID_CTRLR_OACS_FW) == 0) 256 errx(1, 257 "controller does not support firmware activate/download"); 258 259 if (f_flag && slot == 1 && (cdata.frmw & NVME_ID_CTRLR_FRMW_SLOT1_RO)) 260 errx(1, "slot %d is marked as read only", slot); 261 262 if (slot > __SHIFTOUT(cdata.frmw, NVME_ID_CTRLR_FRMW_NSLOT)) 263 errx(1, 264 "slot %d specified but controller only supports %d slots", 265 slot, 266 (uint8_t)__SHIFTOUT(cdata.frmw, NVME_ID_CTRLR_FRMW_NSLOT)); 267 268 if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot)) 269 errx(1, 270 "slot %d does not contain valid firmware,\n" 271 "try 'nvmecontrol logpage -p 3 %s' to get a list " 272 "of available images\n", 273 slot, controller); 274 275 if (f_flag) 276 read_image_file(image, &buf, &size); 277 278 if (f_flag && a_flag) 279 printf("You are about to download and activate " 280 "firmware image (%s) to controller %s.\n" 281 "This may damage your controller and/or " 282 "overwrite an existing firmware image.\n", 283 image, controller); 284 else if (a_flag) 285 printf("You are about to activate a new firmware " 286 "image on controller %s.\n" 287 "This may damage your controller.\n", 288 controller); 289 else if (f_flag) 290 printf("You are about to download firmware image " 291 "(%s) to controller %s.\n" 292 "This may damage your controller and/or " 293 "overwrite an existing firmware image.\n", 294 image, controller); 295 296 printf("Are you sure you want to continue? (yes/no) "); 297 while (1) { 298 fgets(prompt, sizeof(prompt), stdin); 299 if (strncasecmp(prompt, "yes", 3) == 0) 300 break; 301 if (strncasecmp(prompt, "no", 2) == 0) 302 exit(1); 303 printf("Please answer \"yes\" or \"no\". "); 304 } 305 306 if (f_flag) { 307 update_firmware(fd, buf, size); 308 if (a_flag) 309 commit_action = NVME_COMMIT_ACTION_REPLACE_ACTIVATE; 310 else 311 commit_action = NVME_COMMIT_ACTION_REPLACE_NO_ACTIVATE; 312 } else { 313 commit_action = NVME_COMMIT_ACTION_ACTIVATE_RESET; 314 } 315 316 reboot_required = activate_firmware(fd, slot, commit_action); 317 318 if (a_flag) { 319 if (reboot_required) { 320 printf("New firmware image activated but requires " 321 "conventional reset (i.e. reboot) to " 322 "complete activation.\n"); 323 } else { 324 printf("New firmware image activated and will take " 325 "effect after next controller reset.\n" 326 "Controller reset can be initiated via " 327 "'nvmecontrol reset %s'\n", 328 controller); 329 } 330 } 331 332 close(fd); 333 exit(0); 334} 335#endif 336