fwdownload.c revision 227961
1227961Semaste/*- 2227961Semaste * Copyright (c) 2011 Sandvine Incorporated. All rights reserved. 3227961Semaste * Copyright (c) 2002-2011 Andre Albsmeier <andre@albsmeier.net> 4227961Semaste * All rights reserved. 5227961Semaste * 6227961Semaste * Redistribution and use in source and binary forms, with or without 7227961Semaste * modification, are permitted provided that the following conditions 8227961Semaste * are met: 9227961Semaste * 1. Redistributions of source code must retain the above copyright 10227961Semaste * notice, this list of conditions and the following disclaimer, 11227961Semaste * without modification, immediately at the beginning of the file. 12227961Semaste * 2. Redistributions in binary form must reproduce the above copyright 13227961Semaste * notice, this list of conditions and the following disclaimer in the 14227961Semaste * documentation and/or other materials provided with the distribution. 15227961Semaste * 16227961Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17227961Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18227961Semaste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19227961Semaste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20227961Semaste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21227961Semaste * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22227961Semaste * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23227961Semaste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24227961Semaste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25227961Semaste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26227961Semaste */ 27227961Semaste 28227961Semaste/* 29227961Semaste * BEWARE: 30227961Semaste * 31227961Semaste * The fact that you see your favorite vendor listed below does not 32227961Semaste * imply that your equipment won't break when you use this software 33227961Semaste * with it. It only means that the firmware of at least one device type 34227961Semaste * of each vendor listed has been programmed successfully using this code. 35227961Semaste * 36227961Semaste * The -s option simulates a download but does nothing apart from that. 37227961Semaste * It can be used to check what chunk sizes would have been used with the 38227961Semaste * specified device. 39227961Semaste */ 40227961Semaste 41227961Semaste#include <sys/cdefs.h> 42227961Semaste__FBSDID("$FreeBSD: head/sbin/camcontrol/fwdownload.c 227961 2011-11-25 04:03:37Z emaste $"); 43227961Semaste 44227961Semaste#include <sys/types.h> 45227961Semaste#include <sys/stat.h> 46227961Semaste 47227961Semaste#include <err.h> 48227961Semaste#include <fcntl.h> 49227961Semaste#include <stdio.h> 50227961Semaste#include <stdlib.h> 51227961Semaste#include <string.h> 52227961Semaste#include <unistd.h> 53227961Semaste 54227961Semaste#include <cam/scsi/scsi_all.h> 55227961Semaste#include <cam/scsi/scsi_message.h> 56227961Semaste#include <camlib.h> 57227961Semaste 58227961Semaste#include "camcontrol.h" 59227961Semaste 60227961Semaste#define CMD_TIMEOUT 50000 /* 50 seconds */ 61227961Semaste 62227961Semastetypedef enum { 63227961Semaste VENDOR_HITACHI, 64227961Semaste VENDOR_HP, 65227961Semaste VENDOR_IBM, 66227961Semaste VENDOR_PLEXTOR, 67227961Semaste VENDOR_QUANTUM, 68227961Semaste VENDOR_SEAGATE, 69227961Semaste VENDOR_UNKNOWN 70227961Semaste} fw_vendor_t; 71227961Semaste 72227961Semastestruct fw_vendor { 73227961Semaste fw_vendor_t type; 74227961Semaste const char *pattern; 75227961Semaste int max_pkt_size; 76227961Semaste u_int8_t cdb_byte2; 77227961Semaste u_int8_t cdb_byte2_last; 78227961Semaste int inc_cdb_buffer_id; 79227961Semaste int inc_cdb_offset; 80227961Semaste}; 81227961Semaste 82227961Semastestruct fw_vendor vendors_list[] = { 83227961Semaste {VENDOR_HITACHI, "HITACHI", 0x8000, 0x05, 0x05, 1, 0}, 84227961Semaste {VENDOR_HP, "HP", 0x8000, 0x07, 0x07, 0, 1}, 85227961Semaste {VENDOR_IBM, "IBM", 0x8000, 0x05, 0x05, 1, 0}, 86227961Semaste {VENDOR_PLEXTOR, "PLEXTOR", 0x2000, 0x04, 0x05, 0, 1}, 87227961Semaste {VENDOR_QUANTUM, "QUANTUM", 0x2000, 0x04, 0x05, 0, 1}, 88227961Semaste {VENDOR_SEAGATE, "SEAGATE", 0x8000, 0x07, 0x07, 0, 1}, 89227961Semaste {VENDOR_UNKNOWN, NULL, 0x0000, 0x00, 0x00, 0, 0} 90227961Semaste}; 91227961Semaste 92227961Semastestatic struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev); 93227961Semastestatic char *fw_read_img(char *fw_img_path, struct fw_vendor *vp, 94227961Semaste int *num_bytes); 95227961Semastestatic int fw_download_img(struct cam_device *cam_dev, 96227961Semaste struct fw_vendor *vp, char *buf, int img_size, 97227961Semaste int sim_mode, int verbose, int retry_count, int timeout); 98227961Semaste 99227961Semaste/* 100227961Semaste * Find entry in vendors list that belongs to 101227961Semaste * the vendor of given cam device. 102227961Semaste */ 103227961Semastestatic struct fw_vendor * 104227961Semastefw_get_vendor(struct cam_device *cam_dev) 105227961Semaste{ 106227961Semaste char vendor[SID_VENDOR_SIZE + 1]; 107227961Semaste struct fw_vendor *vp; 108227961Semaste 109227961Semaste if (cam_dev == NULL) 110227961Semaste return (NULL); 111227961Semaste cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor, 112227961Semaste sizeof(cam_dev->inq_data.vendor), sizeof(vendor)); 113227961Semaste for (vp = vendors_list; vp->pattern != NULL; vp++) { 114227961Semaste if (!cam_strmatch((const u_char *)vendor, 115227961Semaste (const u_char *)vp->pattern, strlen(vendor))) 116227961Semaste break; 117227961Semaste } 118227961Semaste return (vp); 119227961Semaste} 120227961Semaste 121227961Semaste/* 122227961Semaste * Allocate a buffer and read fw image file into it 123227961Semaste * from given path. Number of bytes read is stored 124227961Semaste * in num_bytes. 125227961Semaste */ 126227961Semastestatic char * 127227961Semastefw_read_img(char *fw_img_path, struct fw_vendor *vp, int *num_bytes) 128227961Semaste{ 129227961Semaste int fd; 130227961Semaste struct stat stbuf; 131227961Semaste char *buf; 132227961Semaste off_t img_size; 133227961Semaste int skip_bytes = 0; 134227961Semaste 135227961Semaste if ((fd = open(fw_img_path, O_RDONLY)) < 0) { 136227961Semaste warn("Could not open image file %s", fw_img_path); 137227961Semaste return (NULL); 138227961Semaste } 139227961Semaste if (fstat(fd, &stbuf) < 0) { 140227961Semaste warn("Could not stat image file %s", fw_img_path); 141227961Semaste goto bailout1; 142227961Semaste } 143227961Semaste if ((img_size = stbuf.st_size) == 0) { 144227961Semaste warnx("Zero length image file %s", fw_img_path); 145227961Semaste goto bailout1; 146227961Semaste } 147227961Semaste if ((buf = malloc(img_size)) == NULL) { 148227961Semaste warnx("Could not allocate buffer to read image file %s", 149227961Semaste fw_img_path); 150227961Semaste goto bailout1; 151227961Semaste } 152227961Semaste /* Skip headers if applicable. */ 153227961Semaste switch (vp->type) { 154227961Semaste case VENDOR_SEAGATE: 155227961Semaste if (read(fd, buf, 16) != 16) { 156227961Semaste warn("Could not read image file %s", fw_img_path); 157227961Semaste goto bailout; 158227961Semaste } 159227961Semaste if (lseek(fd, 0, SEEK_SET) == -1) { 160227961Semaste warn("Unable to lseek"); 161227961Semaste goto bailout; 162227961Semaste } 163227961Semaste if ((strncmp(buf, "SEAGATE,SEAGATE ", 16) == 0) || 164227961Semaste (img_size % 512 == 80)) 165227961Semaste skip_bytes = 80; 166227961Semaste break; 167227961Semaste default: 168227961Semaste break; 169227961Semaste } 170227961Semaste if (skip_bytes != 0) { 171227961Semaste fprintf(stdout, "Skipping %d byte header.\n", skip_bytes); 172227961Semaste if (lseek(fd, skip_bytes, SEEK_SET) == -1) { 173227961Semaste warn("Could not lseek"); 174227961Semaste goto bailout; 175227961Semaste } 176227961Semaste img_size -= skip_bytes; 177227961Semaste } 178227961Semaste /* Read image into a buffer. */ 179227961Semaste if (read(fd, buf, img_size) != img_size) { 180227961Semaste warn("Could not read image file %s", fw_img_path); 181227961Semaste goto bailout; 182227961Semaste } 183227961Semaste *num_bytes = img_size; 184227961Semaste return (buf); 185227961Semastebailout: 186227961Semaste free(buf); 187227961Semastebailout1: 188227961Semaste close(fd); 189227961Semaste *num_bytes = 0; 190227961Semaste return (NULL); 191227961Semaste} 192227961Semaste 193227961Semaste/* 194227961Semaste * Download firmware stored in buf to cam_dev. If simulation mode 195227961Semaste * is enabled, only show what packet sizes would be sent to the 196227961Semaste * device but do not sent any actual packets 197227961Semaste */ 198227961Semastestatic int 199227961Semastefw_download_img(struct cam_device *cam_dev, struct fw_vendor *vp, 200227961Semaste char *buf, int img_size, int sim_mode, int verbose, int retry_count, 201227961Semaste int timeout) 202227961Semaste{ 203227961Semaste struct scsi_write_buffer cdb; 204227961Semaste union ccb *ccb; 205227961Semaste int pkt_count = 0; 206227961Semaste u_int32_t pkt_size = 0; 207227961Semaste char *pkt_ptr = buf; 208227961Semaste u_int32_t offset; 209227961Semaste int last_pkt = 0; 210227961Semaste 211227961Semaste if ((ccb = cam_getccb(cam_dev)) == NULL) { 212227961Semaste warnx("Could not allocate CCB"); 213227961Semaste return (1); 214227961Semaste } 215227961Semaste scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, 216227961Semaste SSD_FULL_SIZE, 5000); 217227961Semaste /* Disable freezing the device queue. */ 218227961Semaste ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 219227961Semaste if (cam_send_ccb(cam_dev, ccb) < 0) { 220227961Semaste warnx("Error sending test unit ready"); 221227961Semaste if (verbose) 222227961Semaste cam_error_print(cam_dev, ccb, CAM_ESF_ALL, 223227961Semaste CAM_EPF_ALL, stderr); 224227961Semaste cam_freeccb(ccb); 225227961Semaste return(1); 226227961Semaste } 227227961Semaste if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 228227961Semaste warnx("Device is not ready"); 229227961Semaste if (verbose) 230227961Semaste cam_error_print(cam_dev, ccb, CAM_ESF_ALL, 231227961Semaste CAM_EPF_ALL, stderr); 232227961Semaste cam_freeccb(ccb); 233227961Semaste return (1); 234227961Semaste } 235227961Semaste pkt_size = vp->max_pkt_size; 236227961Semaste if (verbose || sim_mode) { 237227961Semaste fprintf(stdout, 238227961Semaste "--------------------------------------------------\n"); 239227961Semaste fprintf(stdout, 240227961Semaste "PktNo. PktSize BytesRemaining LastPkt\n"); 241227961Semaste fprintf(stdout, 242227961Semaste "--------------------------------------------------\n"); 243227961Semaste } 244227961Semaste /* Download single fw packets. */ 245227961Semaste do { 246227961Semaste if (img_size <= vp->max_pkt_size) { 247227961Semaste last_pkt = 1; 248227961Semaste pkt_size = img_size; 249227961Semaste } 250227961Semaste if (verbose || sim_mode) 251227961Semaste fprintf(stdout, "%3u %5u (0x%05X) %7u (0x%06X) " 252227961Semaste "%d\n", pkt_count, pkt_size, pkt_size, 253227961Semaste img_size - pkt_size, img_size - pkt_size, 254227961Semaste last_pkt); 255227961Semaste bzero(&cdb, sizeof(cdb)); 256227961Semaste cdb.opcode = WRITE_BUFFER; 257227961Semaste cdb.control = 0; 258227961Semaste /* Parameter list length. */ 259227961Semaste scsi_ulto3b(pkt_size, &cdb.length[0]); 260227961Semaste offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0; 261227961Semaste scsi_ulto3b(offset, &cdb.offset[0]); 262227961Semaste cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2; 263227961Semaste cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0; 264227961Semaste /* Zero out payload of ccb union after ccb header. */ 265227961Semaste bzero((u_char *)ccb + sizeof(struct ccb_hdr), 266227961Semaste sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); 267227961Semaste /* Copy previously constructed cdb into ccb_scsiio struct. */ 268227961Semaste bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0], 269227961Semaste sizeof(struct scsi_write_buffer)); 270227961Semaste /* Fill rest of ccb_scsiio struct. */ 271227961Semaste if (!sim_mode) { 272227961Semaste cam_fill_csio(&ccb->csio, /* ccb_scsiio */ 273227961Semaste retry_count, /* retries */ 274227961Semaste NULL, /* cbfcnp */ 275227961Semaste CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */ 276227961Semaste CAM_TAG_ACTION_NONE, /* tag_action */ 277227961Semaste (u_char *)pkt_ptr, /* data_ptr */ 278227961Semaste pkt_size, /* dxfer_len */ 279227961Semaste SSD_FULL_SIZE, /* sense_len */ 280227961Semaste sizeof(struct scsi_write_buffer), /* cdb_len */ 281227961Semaste timeout ? timeout : CMD_TIMEOUT); /* timeout */ 282227961Semaste /* Execute the command. */ 283227961Semaste if (cam_send_ccb(cam_dev, ccb) < 0) { 284227961Semaste warnx("Error writing image to device"); 285227961Semaste if (verbose) 286227961Semaste cam_error_print(cam_dev, ccb, CAM_ESF_ALL, 287227961Semaste CAM_EPF_ALL, stderr); 288227961Semaste goto bailout; 289227961Semaste } 290227961Semaste } 291227961Semaste /* Prepare next round. */ 292227961Semaste pkt_count++; 293227961Semaste pkt_ptr += pkt_size; 294227961Semaste img_size -= pkt_size; 295227961Semaste } while(!last_pkt); 296227961Semaste if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 297227961Semaste if (verbose) 298227961Semaste cam_error_print(cam_dev, ccb, CAM_ESF_ALL, 299227961Semaste CAM_EPF_ALL, stderr); 300227961Semaste goto bailout; 301227961Semaste } 302227961Semaste cam_freeccb(ccb); 303227961Semaste return (0); 304227961Semastebailout: 305227961Semaste cam_freeccb(ccb); 306227961Semaste return (1); 307227961Semaste} 308227961Semaste 309227961Semasteint 310227961Semastefwdownload(struct cam_device *device, int argc, char **argv, 311227961Semaste char *combinedopt, int verbose, int retry_count, int timeout) 312227961Semaste{ 313227961Semaste struct fw_vendor *vp; 314227961Semaste char *fw_img_path = NULL; 315227961Semaste char *buf; 316227961Semaste int img_size; 317227961Semaste int c; 318227961Semaste int sim_mode = 0; 319227961Semaste int confirmed = 0; 320227961Semaste 321227961Semaste while ((c = getopt(argc, argv, combinedopt)) != -1) { 322227961Semaste switch (c) { 323227961Semaste case 's': 324227961Semaste sim_mode = 1; 325227961Semaste confirmed = 1; 326227961Semaste break; 327227961Semaste case 'f': 328227961Semaste fw_img_path = optarg; 329227961Semaste break; 330227961Semaste case 'y': 331227961Semaste confirmed = 1; 332227961Semaste break; 333227961Semaste default: 334227961Semaste break; 335227961Semaste } 336227961Semaste } 337227961Semaste 338227961Semaste if (fw_img_path == NULL) 339227961Semaste errx(1, 340227961Semaste "you must specify a firmware image file using -f option"); 341227961Semaste 342227961Semaste vp = fw_get_vendor(device); 343227961Semaste if (vp == NULL || vp->type == VENDOR_UNKNOWN) 344227961Semaste errx(1, "Unsupported device"); 345227961Semaste 346227961Semaste buf = fw_read_img(fw_img_path, vp, &img_size); 347227961Semaste if (buf == NULL) 348227961Semaste goto fail; 349227961Semaste 350227961Semaste if (!confirmed) { 351227961Semaste fprintf(stdout, "You are about to download firmware image (%s)" 352227961Semaste " into the following device:\n", 353227961Semaste fw_img_path); 354227961Semaste if (scsidoinquiry(device, argc, argv, combinedopt, 0, 355227961Semaste 5000) != 0) { 356227961Semaste warnx("Error sending inquiry"); 357227961Semaste goto fail; 358227961Semaste } 359227961Semaste fprintf(stdout, "\nIt may damage your drive. "); 360227961Semaste if (!get_confirmation()) 361227961Semaste goto fail; 362227961Semaste } 363227961Semaste if (sim_mode) 364227961Semaste fprintf(stdout, "Running in simulation mode\n"); 365227961Semaste 366227961Semaste if (fw_download_img(device, vp, buf, img_size, sim_mode, verbose, 367227961Semaste retry_count, timeout) != 0) { 368227961Semaste fprintf(stderr, "Firmware download failed\n"); 369227961Semaste goto fail; 370227961Semaste } else 371227961Semaste fprintf(stdout, "Firmware download successful\n"); 372227961Semaste 373227961Semaste free(buf); 374227961Semaste return (0); 375227961Semastefail: 376227961Semaste if (buf != NULL) 377227961Semaste free(buf); 378227961Semaste return (1); 379227961Semaste} 380227961Semaste 381