firmware.c revision 253393
1187767Sluigi/*- 2187767Sluigi * Copyright (c) 2013 EMC Corp. 3187767Sluigi * All rights reserved. 4187767Sluigi * 5187767Sluigi * Copyright (C) 2012-2013 Intel Corporation 6187767Sluigi * All rights reserved. 7187767Sluigi * 8187767Sluigi * Redistribution and use in source and binary forms, with or without 9187767Sluigi * modification, are permitted provided that the following conditions 10187767Sluigi * are met: 11187767Sluigi * 1. Redistributions of source code must retain the above copyright 12187767Sluigi * notice, this list of conditions and the following disclaimer. 13187767Sluigi * 2. Redistributions in binary form must reproduce the above copyright 14187767Sluigi * notice, this list of conditions and the following disclaimer in the 15187767Sluigi * documentation and/or other materials provided with the distribution. 16187767Sluigi * 17187767Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18187767Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19187767Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20187767Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21187767Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22187767Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23187767Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24187767Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25187767Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26187767Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27187767Sluigi * SUCH DAMAGE. 28187767Sluigi */ 29187767Sluigi 30187767Sluigi#include <sys/cdefs.h> 31187767Sluigi__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 253393 2013-07-16 15:45:37Z jimharris $"); 32187767Sluigi 33187767Sluigi#include <sys/param.h> 34187767Sluigi#include <sys/ioccom.h> 35187767Sluigi#include <sys/stat.h> 36187767Sluigi#include <sys/types.h> 37187767Sluigi 38204591Sluigi#include <ctype.h> 39187767Sluigi#include <err.h> 40187767Sluigi#include <fcntl.h> 41187767Sluigi#include <inttypes.h> 42187767Sluigi#include <stdbool.h> 43187767Sluigi#include <stddef.h> 44187767Sluigi#include <stdio.h> 45187767Sluigi#include <stdlib.h> 46187767Sluigi#include <string.h> 47187767Sluigi#include <unistd.h> 48187767Sluigi 49346205Sae#include "nvmecontrol.h" 50346205Sae 51187767Sluigistatic int 52187767Sluigislot_has_valid_firmware(int fd, int slot) 53187767Sluigi{ 54187767Sluigi struct nvme_firmware_page fw; 55187767Sluigi int has_fw = false; 56187767Sluigi 57187767Sluigi read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, 58187767Sluigi NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw)); 59332400Sae 60332400Sae if (fw.revision[slot-1] != 0LLU) 61332400Sae has_fw = true; 62332400Sae 63332400Sae return (has_fw); 64332400Sae} 65187767Sluigi 66187767Sluigistatic void 67187767Sluigiread_image_file(char *path, void **buf, int32_t *size) 68187767Sluigi{ 69187767Sluigi struct stat sb; 70187767Sluigi int32_t filesize; 71187767Sluigi int fd; 72187767Sluigi 73187767Sluigi *size = 0; 74187767Sluigi *buf = NULL; 75187767Sluigi 76187767Sluigi if ((fd = open(path, O_RDONLY)) < 0) 77187767Sluigi err(1, "unable to open '%s'", path); 78187767Sluigi if (fstat(fd, &sb) < 0) 79187767Sluigi err(1, "unable to stat '%s'", path); 80270424Smelifaro 81270424Smelifaro /* 82187769Sluigi * The NVMe spec does not explicitly state a maximum firmware image 83187769Sluigi * size, although one can be inferred from the dword size limitation 84187769Sluigi * for the size and offset fields in the Firmware Image Download 85187769Sluigi * command. 86187769Sluigi * 87187769Sluigi * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the 88187769Sluigi * size and offsets are specified in terms of dwords (not bytes), but 89187769Sluigi * realistically INT32_MAX is sufficient here and simplifies matters 90332229Stuexen * a bit. 91332229Stuexen */ 92187769Sluigi if (sb.st_size > INT32_MAX) 93187769Sluigi errx(1, "size of file '%s' is too large (%jd bytes)", 94298016Sae path, (intmax_t)sb.st_size); 95187769Sluigi filesize = (int32_t)sb.st_size; 96204591Sluigi if ((*buf = malloc(filesize)) == NULL) 97187769Sluigi errx(1, "unable to malloc %d bytes", filesize); 98204591Sluigi if ((*size = read(fd, *buf, filesize)) < 0) 99204591Sluigi err(1, "error reading '%s'", path); 100187769Sluigi /* XXX assuming no short reads */ 101187769Sluigi if (*size != filesize) 102187769Sluigi errx(1, 103187769Sluigi "error reading '%s' (read %d bytes, requested %d bytes)", 104187769Sluigi path, *size, filesize); 105187769Sluigi} 106187769Sluigi 107187769Sluigistatic void 108187769Sluigiupdate_firmware(int fd, uint8_t *payload, int32_t payload_size) 109187769Sluigi{ 110187769Sluigi struct nvme_pt_command pt; 111187769Sluigi int32_t off, resid, size; 112190633Spiso void *chunk; 113223666Sae 114223666Sae off = 0; 115187769Sluigi resid = payload_size; 116187769Sluigi 117187769Sluigi if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL) 118187769Sluigi errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); 119187769Sluigi 120187769Sluigi while (resid > 0) { 121187769Sluigi size = (resid >= NVME_MAX_XFER_SIZE) ? 122187769Sluigi NVME_MAX_XFER_SIZE : resid; 123187769Sluigi memcpy(chunk, payload + off, size); 124187769Sluigi 125187769Sluigi memset(&pt, 0, sizeof(pt)); 126187769Sluigi pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; 127337461Sae pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1; 128187769Sluigi pt.cmd.cdw11 = (off / sizeof(uint32_t)); 129337461Sae pt.buf = chunk; 130187769Sluigi pt.len = size; 131187769Sluigi pt.is_read = 0; 132187769Sluigi 133187769Sluigi if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 134187769Sluigi err(1, "firmware download request failed"); 135187769Sluigi 136187769Sluigi if (nvme_completion_is_error(&pt.cpl)) 137187769Sluigi errx(1, "firmware download request returned error"); 138187769Sluigi 139187769Sluigi resid -= size; 140187769Sluigi off += size; 141187769Sluigi } 142187769Sluigi} 143205169Sluigi 144187769Sluigistatic void 145187769Sluigiactivate_firmware(int fd, int slot, int activate_action) 146187769Sluigi{ 147187769Sluigi struct nvme_pt_command pt; 148187769Sluigi 149187769Sluigi memset(&pt, 0, sizeof(pt)); 150187769Sluigi pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; 151187769Sluigi pt.cmd.cdw10 = (activate_action << 3) | slot; 152187769Sluigi pt.is_read = 0; 153187769Sluigi 154187769Sluigi if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 155187769Sluigi err(1, "firmware activate request failed"); 156187769Sluigi 157187769Sluigi if (nvme_completion_is_error(&pt.cpl)) 158187769Sluigi errx(1, "firmware activate request returned error"); 159187769Sluigi} 160187769Sluigi 161187769Sluigistatic void 162187769Sluigifirmware_usage(void) 163187769Sluigi{ 164187769Sluigi fprintf(stderr, "usage:\n"); 165187769Sluigi fprintf(stderr, FIRMWARE_USAGE); 166187769Sluigi exit(1); 167187769Sluigi} 168187769Sluigi 169187769Sluigivoid 170187769Sluigifirmware(int argc, char *argv[]) 171187769Sluigi{ 172187769Sluigi int fd = -1, slot = 0; 173204591Sluigi int a_flag, s_flag, f_flag; 174204591Sluigi char ch, *p, *image = NULL; 175187769Sluigi char *controller = NULL, prompt[64]; 176187769Sluigi void *buf = NULL; 177204591Sluigi int32_t size = 0; 178194930Soleg struct nvme_controller_data cdata; 179187769Sluigi 180187769Sluigi a_flag = s_flag = f_flag = false; 181266941Shiren 182187769Sluigi while ((ch = getopt(argc, argv, "af:s:")) != -1) { 183187769Sluigi switch (ch) { 184300779Struckman case 'a': 185300779Struckman a_flag = true; 186300779Struckman break; 187300779Struckman case 's': 188300779Struckman slot = strtol(optarg, &p, 0); 189300779Struckman if (p != NULL && *p != '\0') { 190300779Struckman fprintf(stderr, 191300779Struckman "\"%s\" not valid slot.\n", 192300779Struckman optarg); 193300779Struckman firmware_usage(); 194300779Struckman } else if (slot == 0) { 195300779Struckman fprintf(stderr, 196300779Struckman "0 is not a valid slot number. " 197300779Struckman "Slot numbers start at 1.\n"); 198300779Struckman firmware_usage(); 199300779Struckman } else if (slot > 7) { 200300779Struckman fprintf(stderr, 201300779Struckman "Slot number %s specified which is " 202300779Struckman "greater than max allowed slot number of " 203300779Struckman "7.\n", optarg); 204300779Struckman firmware_usage(); 205300779Struckman } 206300779Struckman s_flag = true; 207300779Struckman break; 208300779Struckman case 'f': 209204591Sluigi image = optarg; 210187769Sluigi f_flag = true; 211204591Sluigi break; 212204591Sluigi } 213204591Sluigi } 214204591Sluigi 215204591Sluigi /* Check that a controller (and not a namespace) was specified. */ 216187769Sluigi if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL) 217187769Sluigi firmware_usage(); 218332210Stuexen 219332210Stuexen if (!f_flag && !a_flag) { 220332210Stuexen fprintf(stderr, 221332210Stuexen "Neither a replace ([-f path_to_firmware]) nor " 222223080Sae "activate ([-a]) firmware image action\n" 223332210Stuexen "was specified.\n"); 224332210Stuexen firmware_usage(); 225332210Stuexen } 226187769Sluigi 227187769Sluigi if (!f_flag && a_flag && slot == 0) { 228220804Sglebius fprintf(stderr, 229187769Sluigi "Slot number to activate not specified.\n"); 230187769Sluigi firmware_usage(); 231187769Sluigi } 232187769Sluigi 233187769Sluigi controller = argv[optind]; 234187769Sluigi open_dev(controller, &fd, 1, 1); 235187769Sluigi read_controller_data(fd, &cdata); 236187769Sluigi 237187769Sluigi if (cdata.oacs.firmware == 0) 238187769Sluigi errx(1, 239187769Sluigi "controller does not support firmware activate/download"); 240187769Sluigi 241187769Sluigi if (f_flag && slot == 1 && cdata.frmw.slot1_ro) 242187769Sluigi errx(1, "slot %d is marked as read only", slot); 243200567Sluigi 244215179Sluigi if (slot > cdata.frmw.num_slots) 245248552Smelifaro errx(1, 246272840Smelifaro "slot %d specified but controller only supports %d slots", 247272840Smelifaro slot, cdata.frmw.num_slots); 248272840Smelifaro 249272840Smelifaro if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot)) 250272840Smelifaro errx(1, 251272840Smelifaro "slot %d does not contain valid firmware,\n" 252272840Smelifaro "try 'nvmecontrol logpage -p 3 %s' to get a list " 253272840Smelifaro "of available images\n", 254272840Smelifaro slot, controller); 255272840Smelifaro 256272840Smelifaro if (f_flag) 257272840Smelifaro read_image_file(image, &buf, &size); 258272840Smelifaro 259272840Smelifaro if (f_flag && a_flag) 260272840Smelifaro printf("You are about to download and activate " 261272840Smelifaro "firmware image (%s) to controller %s.\n" 262272840Smelifaro "This may damage your controller and/or " 263272840Smelifaro "overwrite an existing firmware image.\n", 264272840Smelifaro image, controller); 265272840Smelifaro else if (a_flag) 266290330Sae printf("You are about to activate a new firmware " 267316446Sae "image on controller %s.\n" 268316446Sae "This may damage your controller.\n", 269316446Sae controller); 270316446Sae else if (f_flag) 271316444Sae printf("You are about to download firmware image " 272316446Sae "(%s) to controller %s.\n" 273316446Sae "This may damage your controller and/or " 274316446Sae "overwrite an existing firmware image.\n", 275316446Sae image, controller); 276316446Sae 277316446Sae printf("Are you sure you want to continue? (yes/no) "); 278316446Sae while (1) { 279316446Sae fgets(prompt, sizeof(prompt), stdin); 280316446Sae if (strncasecmp(prompt, "yes", 3) == 0) 281316446Sae break; 282316446Sae if (strncasecmp(prompt, "no", 2) == 0) 283316446Sae exit(1); 284316446Sae printf("Please answer \"yes\" or \"no\". "); 285316446Sae } 286316446Sae 287316446Sae if (f_flag) { 288316446Sae update_firmware(fd, buf, size); 289316446Sae if (a_flag) 290316446Sae activate_firmware(fd, slot, 291346210Sae NVME_AA_REPLACE_ACTIVATE); 292346210Sae else 293316444Sae activate_firmware(fd, slot, 294316444Sae NVME_AA_REPLACE_NO_ACTIVATE); 295316444Sae } else { 296316444Sae activate_firmware(fd, slot, NVME_AA_ACTIVATE); 297316444Sae } 298316444Sae 299317045Sae if (a_flag) { 300317045Sae printf("New firmware image activated and will take " 301337461Sae "effect after next controller reset.\n" 302337461Sae "Controller reset can be initiated via " 303187769Sluigi "'nvmecontrol reset %s'\n", 304272840Smelifaro controller); 305187767Sluigi } 306187767Sluigi 307187767Sluigi close(fd); 308187767Sluigi exit(0); 309204591Sluigi} 310204591Sluigi