1252278Sjimharris/*- 2252278Sjimharris * Copyright (c) 2013 EMC Corp. 3252278Sjimharris * All rights reserved. 4252278Sjimharris * 5252278Sjimharris * Copyright (C) 2012-2013 Intel Corporation 6252278Sjimharris * All rights reserved. 7252278Sjimharris * 8252278Sjimharris * Redistribution and use in source and binary forms, with or without 9252278Sjimharris * modification, are permitted provided that the following conditions 10252278Sjimharris * are met: 11252278Sjimharris * 1. Redistributions of source code must retain the above copyright 12252278Sjimharris * notice, this list of conditions and the following disclaimer. 13252278Sjimharris * 2. Redistributions in binary form must reproduce the above copyright 14252278Sjimharris * notice, this list of conditions and the following disclaimer in the 15252278Sjimharris * documentation and/or other materials provided with the distribution. 16252278Sjimharris * 17252278Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18252278Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19252278Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20252278Sjimharris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21252278Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22252278Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23252278Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24252278Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25252278Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26252278Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27252278Sjimharris * SUCH DAMAGE. 28252278Sjimharris */ 29252278Sjimharris 30252278Sjimharris#include <sys/cdefs.h> 31252278Sjimharris__FBSDID("$FreeBSD$"); 32252278Sjimharris 33252278Sjimharris#include <sys/param.h> 34252278Sjimharris#include <sys/ioccom.h> 35252278Sjimharris#include <sys/stat.h> 36252278Sjimharris#include <sys/types.h> 37252278Sjimharris 38252278Sjimharris#include <ctype.h> 39253109Sjimharris#include <err.h> 40252278Sjimharris#include <fcntl.h> 41253109Sjimharris#include <inttypes.h> 42252278Sjimharris#include <stdbool.h> 43252278Sjimharris#include <stddef.h> 44252278Sjimharris#include <stdio.h> 45252278Sjimharris#include <stdlib.h> 46252278Sjimharris#include <string.h> 47252278Sjimharris#include <unistd.h> 48252278Sjimharris 49252278Sjimharris#include "nvmecontrol.h" 50252278Sjimharris 51252278Sjimharrisstatic int 52252278Sjimharrisslot_has_valid_firmware(int fd, int slot) 53252278Sjimharris{ 54252278Sjimharris struct nvme_firmware_page fw; 55252278Sjimharris int has_fw = false; 56252278Sjimharris 57252278Sjimharris read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, 58252278Sjimharris NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw)); 59252278Sjimharris 60252278Sjimharris if (fw.revision[slot-1] != 0LLU) 61252278Sjimharris has_fw = true; 62252278Sjimharris 63252278Sjimharris return (has_fw); 64252278Sjimharris} 65252278Sjimharris 66252278Sjimharrisstatic void 67253109Sjimharrisread_image_file(char *path, void **buf, int32_t *size) 68252278Sjimharris{ 69252278Sjimharris struct stat sb; 70253109Sjimharris int32_t filesize; 71252278Sjimharris int fd; 72252278Sjimharris 73252278Sjimharris *size = 0; 74252278Sjimharris *buf = NULL; 75252278Sjimharris 76253109Sjimharris if ((fd = open(path, O_RDONLY)) < 0) 77253109Sjimharris err(1, "unable to open '%s'", path); 78253109Sjimharris if (fstat(fd, &sb) < 0) 79253109Sjimharris err(1, "unable to stat '%s'", path); 80253109Sjimharris 81253109Sjimharris /* 82253109Sjimharris * The NVMe spec does not explicitly state a maximum firmware image 83253109Sjimharris * size, although one can be inferred from the dword size limitation 84253109Sjimharris * for the size and offset fields in the Firmware Image Download 85253109Sjimharris * command. 86253109Sjimharris * 87253109Sjimharris * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the 88253109Sjimharris * size and offsets are specified in terms of dwords (not bytes), but 89253109Sjimharris * realistically INT32_MAX is sufficient here and simplifies matters 90253109Sjimharris * a bit. 91253109Sjimharris */ 92253109Sjimharris if (sb.st_size > INT32_MAX) 93253109Sjimharris errx(1, "size of file '%s' is too large (%jd bytes)", 94253109Sjimharris path, (intmax_t)sb.st_size); 95253109Sjimharris filesize = (int32_t)sb.st_size; 96253109Sjimharris if ((*buf = malloc(filesize)) == NULL) 97253279Sjimharris errx(1, "unable to malloc %d bytes", filesize); 98253109Sjimharris if ((*size = read(fd, *buf, filesize)) < 0) 99253109Sjimharris err(1, "error reading '%s'", path); 100253109Sjimharris /* XXX assuming no short reads */ 101253109Sjimharris if (*size != filesize) 102253109Sjimharris errx(1, 103253109Sjimharris "error reading '%s' (read %d bytes, requested %d bytes)", 104253109Sjimharris path, *size, filesize); 105252278Sjimharris} 106252278Sjimharris 107252278Sjimharrisstatic void 108253109Sjimharrisupdate_firmware(int fd, uint8_t *payload, int32_t payload_size) 109252278Sjimharris{ 110252278Sjimharris struct nvme_pt_command pt; 111253109Sjimharris int32_t off, resid, size; 112252278Sjimharris void *chunk; 113252278Sjimharris 114252278Sjimharris off = 0; 115252278Sjimharris resid = payload_size; 116252278Sjimharris 117253109Sjimharris if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL) 118253109Sjimharris errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); 119252278Sjimharris 120252278Sjimharris while (resid > 0) { 121252278Sjimharris size = (resid >= NVME_MAX_XFER_SIZE) ? 122252278Sjimharris NVME_MAX_XFER_SIZE : resid; 123252278Sjimharris memcpy(chunk, payload + off, size); 124252278Sjimharris 125252278Sjimharris memset(&pt, 0, sizeof(pt)); 126252278Sjimharris pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; 127252278Sjimharris pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1; 128252278Sjimharris pt.cmd.cdw11 = (off / sizeof(uint32_t)); 129252278Sjimharris pt.buf = chunk; 130252278Sjimharris pt.len = size; 131252278Sjimharris pt.is_read = 0; 132252278Sjimharris 133253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 134253109Sjimharris err(1, "firmware download request failed"); 135252278Sjimharris 136253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 137253109Sjimharris errx(1, "firmware download request returned error"); 138252278Sjimharris 139252278Sjimharris resid -= size; 140252278Sjimharris off += size; 141252278Sjimharris } 142252278Sjimharris} 143252278Sjimharris 144265567Sjimharrisstatic int 145252278Sjimharrisactivate_firmware(int fd, int slot, int activate_action) 146252278Sjimharris{ 147252278Sjimharris struct nvme_pt_command pt; 148252278Sjimharris 149252278Sjimharris memset(&pt, 0, sizeof(pt)); 150252278Sjimharris pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; 151252278Sjimharris pt.cmd.cdw10 = (activate_action << 3) | slot; 152252278Sjimharris pt.is_read = 0; 153252278Sjimharris 154253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 155253109Sjimharris err(1, "firmware activate request failed"); 156252278Sjimharris 157265567Sjimharris if (pt.cpl.status.sct == NVME_SCT_COMMAND_SPECIFIC && 158265567Sjimharris pt.cpl.status.sc == NVME_SC_FIRMWARE_REQUIRES_RESET) 159265567Sjimharris return 1; 160265567Sjimharris 161253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 162253109Sjimharris errx(1, "firmware activate request returned error"); 163265567Sjimharris 164265567Sjimharris return 0; 165252278Sjimharris} 166252278Sjimharris 167252278Sjimharrisstatic void 168252278Sjimharrisfirmware_usage(void) 169252278Sjimharris{ 170252278Sjimharris fprintf(stderr, "usage:\n"); 171252278Sjimharris fprintf(stderr, FIRMWARE_USAGE); 172253109Sjimharris exit(1); 173252278Sjimharris} 174252278Sjimharris 175252278Sjimharrisvoid 176252278Sjimharrisfirmware(int argc, char *argv[]) 177252278Sjimharris{ 178252278Sjimharris int fd = -1, slot = 0; 179252278Sjimharris int a_flag, s_flag, f_flag; 180265567Sjimharris int activate_action, reboot_required; 181252278Sjimharris char ch, *p, *image = NULL; 182252278Sjimharris char *controller = NULL, prompt[64]; 183252278Sjimharris void *buf = NULL; 184253109Sjimharris int32_t size = 0; 185252278Sjimharris struct nvme_controller_data cdata; 186252278Sjimharris 187252278Sjimharris a_flag = s_flag = f_flag = false; 188252278Sjimharris 189252278Sjimharris while ((ch = getopt(argc, argv, "af:s:")) != -1) { 190252278Sjimharris switch (ch) { 191252278Sjimharris case 'a': 192252278Sjimharris a_flag = true; 193252278Sjimharris break; 194252278Sjimharris case 's': 195252278Sjimharris slot = strtol(optarg, &p, 0); 196252278Sjimharris if (p != NULL && *p != '\0') { 197252278Sjimharris fprintf(stderr, 198252278Sjimharris "\"%s\" not valid slot.\n", 199252278Sjimharris optarg); 200252278Sjimharris firmware_usage(); 201252278Sjimharris } else if (slot == 0) { 202252278Sjimharris fprintf(stderr, 203252278Sjimharris "0 is not a valid slot number. " 204252278Sjimharris "Slot numbers start at 1.\n"); 205252278Sjimharris firmware_usage(); 206252278Sjimharris } else if (slot > 7) { 207252278Sjimharris fprintf(stderr, 208252278Sjimharris "Slot number %s specified which is " 209252278Sjimharris "greater than max allowed slot number of " 210252278Sjimharris "7.\n", optarg); 211252278Sjimharris firmware_usage(); 212252278Sjimharris } 213252278Sjimharris s_flag = true; 214252278Sjimharris break; 215252278Sjimharris case 'f': 216252278Sjimharris image = optarg; 217252278Sjimharris f_flag = true; 218252278Sjimharris break; 219252278Sjimharris } 220252278Sjimharris } 221252278Sjimharris 222252278Sjimharris /* Check that a controller (and not a namespace) was specified. */ 223252278Sjimharris if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL) 224252278Sjimharris firmware_usage(); 225252278Sjimharris 226252278Sjimharris if (!f_flag && !a_flag) { 227252278Sjimharris fprintf(stderr, 228252278Sjimharris "Neither a replace ([-f path_to_firmware]) nor " 229252278Sjimharris "activate ([-a]) firmware image action\n" 230252278Sjimharris "was specified.\n"); 231252278Sjimharris firmware_usage(); 232252278Sjimharris } 233252278Sjimharris 234252278Sjimharris if (!f_flag && a_flag && slot == 0) { 235252278Sjimharris fprintf(stderr, 236252278Sjimharris "Slot number to activate not specified.\n"); 237252278Sjimharris firmware_usage(); 238252278Sjimharris } 239252278Sjimharris 240252278Sjimharris controller = argv[optind]; 241252278Sjimharris open_dev(controller, &fd, 1, 1); 242252278Sjimharris read_controller_data(fd, &cdata); 243252278Sjimharris 244253109Sjimharris if (cdata.oacs.firmware == 0) 245253109Sjimharris errx(1, 246253109Sjimharris "controller does not support firmware activate/download"); 247252278Sjimharris 248253109Sjimharris if (f_flag && slot == 1 && cdata.frmw.slot1_ro) 249253109Sjimharris errx(1, "slot %d is marked as read only", slot); 250252278Sjimharris 251253109Sjimharris if (slot > cdata.frmw.num_slots) 252253109Sjimharris errx(1, 253253109Sjimharris "slot %d specified but controller only supports %d slots", 254252278Sjimharris slot, cdata.frmw.num_slots); 255252278Sjimharris 256253393Sjimharris if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot)) 257253109Sjimharris errx(1, 258253109Sjimharris "slot %d does not contain valid firmware,\n" 259253109Sjimharris "try 'nvmecontrol logpage -p 3 %s' to get a list " 260253109Sjimharris "of available images\n", 261252278Sjimharris slot, controller); 262252278Sjimharris 263253110Sjimharris if (f_flag) 264253110Sjimharris read_image_file(image, &buf, &size); 265253110Sjimharris 266252278Sjimharris if (f_flag && a_flag) 267252278Sjimharris printf("You are about to download and activate " 268252278Sjimharris "firmware image (%s) to controller %s.\n" 269252278Sjimharris "This may damage your controller and/or " 270252278Sjimharris "overwrite an existing firmware image.\n", 271252278Sjimharris image, controller); 272252278Sjimharris else if (a_flag) 273252278Sjimharris printf("You are about to activate a new firmware " 274252278Sjimharris "image on controller %s.\n" 275252278Sjimharris "This may damage your controller.\n", 276252278Sjimharris controller); 277252278Sjimharris else if (f_flag) 278252278Sjimharris printf("You are about to download firmware image " 279252278Sjimharris "(%s) to controller %s.\n" 280252278Sjimharris "This may damage your controller and/or " 281252278Sjimharris "overwrite an existing firmware image.\n", 282252278Sjimharris image, controller); 283252278Sjimharris 284252278Sjimharris printf("Are you sure you want to continue? (yes/no) "); 285252278Sjimharris while (1) { 286252278Sjimharris fgets(prompt, sizeof(prompt), stdin); 287252278Sjimharris if (strncasecmp(prompt, "yes", 3) == 0) 288252278Sjimharris break; 289252278Sjimharris if (strncasecmp(prompt, "no", 2) == 0) 290253109Sjimharris exit(1); 291252278Sjimharris printf("Please answer \"yes\" or \"no\". "); 292252278Sjimharris } 293252278Sjimharris 294252278Sjimharris if (f_flag) { 295252278Sjimharris update_firmware(fd, buf, size); 296252278Sjimharris if (a_flag) 297265567Sjimharris activate_action = NVME_AA_REPLACE_ACTIVATE; 298252278Sjimharris else 299265567Sjimharris activate_action = NVME_AA_REPLACE_NO_ACTIVATE; 300252278Sjimharris } else { 301265567Sjimharris activate_action = NVME_AA_ACTIVATE; 302252278Sjimharris } 303252278Sjimharris 304265567Sjimharris reboot_required = activate_firmware(fd, slot, activate_action); 305265567Sjimharris 306252278Sjimharris if (a_flag) { 307265567Sjimharris if (reboot_required) { 308265567Sjimharris printf("New firmware image activated but requires " 309265567Sjimharris "conventional reset (i.e. reboot) to " 310265567Sjimharris "complete activation.\n"); 311265567Sjimharris } else { 312265567Sjimharris printf("New firmware image activated and will take " 313265567Sjimharris "effect after next controller reset.\n" 314265567Sjimharris "Controller reset can be initiated via " 315265567Sjimharris "'nvmecontrol reset %s'\n", 316265567Sjimharris controller); 317265567Sjimharris } 318252278Sjimharris } 319252278Sjimharris 320252278Sjimharris close(fd); 321253109Sjimharris exit(0); 322252278Sjimharris} 323