1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Joyent, Inc. 14 */ 15 16 /* 17 * This is a general firmware flash plugin that does basic verification for 18 * devices backed by sd(4D). 19 * 20 * The sd(4D) target for firmware flashing uses the general SCSI WRITE BUFFER 21 * options with various modes to instruct the drive to download and install 22 * microcode (what SPC-3 calls firmware). To verify that something fits, we can 23 * use the READ BUFFER command with mode 03h to indicate that we want to 24 * buffer's descriptor. This gives us both the buffer's total size and the 25 * required alignment for writes. 26 * 27 * Unfortunately, it's impossible to know for certain if that size is supposed 28 * to be equivalent to the microcode's. While a READ BUFFER is supposed to 29 * return the same data as with a WRITE BUFFER command, experimental evidence 30 * has shown that this isn't always the case. Especially as the firmware buffer 31 * usually leverages buffer zero, but has custom modes to access it. 32 */ 33 34 #include <libintl.h> 35 #include <fwflash/fwflash.h> 36 #include <scsi/libscsi.h> 37 38 /* 39 * The fwflash plugin interface is a bit odd for a modern committed interface 40 * and requires us to refer to data objects in the parent explicitly to get 41 * access to and set various information. It also doesn't allow us a means of 42 * setting data for our transport layer. 43 */ 44 extern struct vrfyplugin *verifier; 45 46 /* 47 * Declare the name of our vendor. This is required by the fwflash 48 * plugin interface. Note it must be a character array. Using a pointer may 49 * confuse the framework and its use of dlsym. 50 */ 51 char vendor[] = "GENERIC"; 52 53 int 54 vendorvrfy(struct devicelist *dvp) 55 { 56 libscsi_hdl_t *hdl = NULL; 57 libscsi_target_t *targ = NULL; 58 libscsi_action_t *act = NULL; 59 libscsi_errno_t serr; 60 spc3_read_buffer_cdb_t *rb_cdb; 61 uint8_t descbuf[4]; 62 uint32_t size; 63 64 int ret = FWFLASH_FAILURE; 65 66 if ((hdl = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) { 67 logmsg(MSG_ERROR, gettext("%s: failed to initialize " 68 "libscsi: %s\n"), 69 verifier->vendor, libscsi_strerror(serr)); 70 return (FWFLASH_FAILURE); 71 } 72 73 if ((targ = libscsi_open(hdl, NULL, dvp->access_devname)) == 74 NULL) { 75 logmsg(MSG_ERROR, 76 gettext("%s: unable to open device %s\n"), 77 verifier->vendor, dvp->access_devname); 78 goto cleanup; 79 } 80 81 if ((act = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER, 82 LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) { 83 logmsg(MSG_ERROR, "%s: failed to alloc scsi action: %s\n", 84 verifier->vendor, libscsi_errmsg(hdl)); 85 goto cleanup; 86 } 87 88 rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(act); 89 90 rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR; 91 92 /* 93 * Microcode upgrade usually only uses the first buffer ID which are 94 * sequentially indexed from zero. Strictly speaking these are all 95 * vendor defined, but so far most vendors we've seen use index zero 96 * for this. 97 */ 98 rb_cdb->rbc_bufferid = 0; 99 100 rb_cdb->rbc_allocation_len[0] = 0; 101 rb_cdb->rbc_allocation_len[1] = 0; 102 rb_cdb->rbc_allocation_len[2] = sizeof (descbuf); 103 104 if (libscsi_exec(act, targ) != 0) { 105 logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer " 106 "descriptor read: %s\n"), verifier->vendor, 107 libscsi_errmsg(hdl)); 108 goto cleanup; 109 } 110 111 if (libscsi_action_get_status(act) != SAM4_STATUS_GOOD) { 112 logmsg(MSG_ERROR, gettext("%s: SCSI READ BUFFER command to " 113 "determine maximum image size failed\n"), verifier->vendor); 114 goto cleanup; 115 } 116 117 if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 && 118 descbuf[3] == 0) { 119 logmsg(MSG_ERROR, gettext("%s: devices %s does not support " 120 "firmware upgrade\n"), verifier->vendor, 121 dvp->access_devname); 122 goto cleanup; 123 } 124 125 size = (descbuf[1] << 16) | (descbuf[2] << 8) | descbuf[3]; 126 logmsg(MSG_INFO, gettext("%s: checking maximum image size %u against " 127 "actual image size: %u\n"), verifier->vendor, size, 128 verifier->imgsize); 129 if (size < verifier->imgsize) { 130 logmsg(MSG_ERROR, gettext("%s: supplied firmware image %s " 131 "exceeds maximum image size of %u\n"), 132 verifier->vendor, verifier->imgfile, size); 133 goto cleanup; 134 } 135 136 logmsg(MSG_INFO, gettext("%s: successfully validated images %s\n"), 137 verifier->vendor, verifier->imgfile); 138 139 verifier->flashbuf = 0; 140 ret = FWFLASH_SUCCESS; 141 cleanup: 142 if (act != NULL) 143 libscsi_action_free(act); 144 if (targ != NULL) 145 libscsi_close(hdl, targ); 146 if (hdl != NULL) 147 libscsi_fini(hdl); 148 149 return (ret); 150 } 151