1d65b419eSXinChen /*
20b4d6575SRobert Mustacchi * This file and its contents are supplied under the terms of the
30b4d6575SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
40b4d6575SRobert Mustacchi * You may only use this file in accordance with the terms of version
50b4d6575SRobert Mustacchi * 1.0 of the CDDL.
6d65b419eSXinChen *
70b4d6575SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
80b4d6575SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
90b4d6575SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10d65b419eSXinChen */
110b4d6575SRobert Mustacchi
12d65b419eSXinChen /*
130b4d6575SRobert Mustacchi * Copyright 2016 Joyent, Inc.
14d65b419eSXinChen */
15d65b419eSXinChen
160b4d6575SRobert Mustacchi /*
170b4d6575SRobert Mustacchi * This is a general firmware flash plugin that does basic verification for
18*bbf21555SRichard Lowe * devices backed by sd(4D).
190b4d6575SRobert Mustacchi *
20*bbf21555SRichard Lowe * The sd(4D) target for firmware flashing uses the general SCSI WRITE BUFFER
210b4d6575SRobert Mustacchi * options with various modes to instruct the drive to download and install
220b4d6575SRobert Mustacchi * microcode (what SPC-3 calls firmware). To verify that something fits, we can
230b4d6575SRobert Mustacchi * use the READ BUFFER command with mode 03h to indicate that we want to
240b4d6575SRobert Mustacchi * buffer's descriptor. This gives us both the buffer's total size and the
250b4d6575SRobert Mustacchi * required alignment for writes.
260b4d6575SRobert Mustacchi *
270b4d6575SRobert Mustacchi * Unfortunately, it's impossible to know for certain if that size is supposed
280b4d6575SRobert Mustacchi * to be equivalent to the microcode's. While a READ BUFFER is supposed to
290b4d6575SRobert Mustacchi * return the same data as with a WRITE BUFFER command, experimental evidence
300b4d6575SRobert Mustacchi * has shown that this isn't always the case. Especially as the firmware buffer
310b4d6575SRobert Mustacchi * usually leverages buffer zero, but has custom modes to access it.
320b4d6575SRobert Mustacchi */
33d65b419eSXinChen
340b4d6575SRobert Mustacchi #include <libintl.h>
35d65b419eSXinChen #include <fwflash/fwflash.h>
360b4d6575SRobert Mustacchi #include <scsi/libscsi.h>
37d65b419eSXinChen
380b4d6575SRobert Mustacchi /*
390b4d6575SRobert Mustacchi * The fwflash plugin interface is a bit odd for a modern committed interface
400b4d6575SRobert Mustacchi * and requires us to refer to data objects in the parent explicitly to get
410b4d6575SRobert Mustacchi * access to and set various information. It also doesn't allow us a means of
420b4d6575SRobert Mustacchi * setting data for our transport layer.
430b4d6575SRobert Mustacchi */
44d65b419eSXinChen extern struct vrfyplugin *verifier;
45d65b419eSXinChen
46d65b419eSXinChen /*
470b4d6575SRobert Mustacchi * Declare the name of our vendor. This is required by the fwflash
480b4d6575SRobert Mustacchi * plugin interface. Note it must be a character array. Using a pointer may
490b4d6575SRobert Mustacchi * confuse the framework and its use of dlsym.
50d65b419eSXinChen */
510b4d6575SRobert Mustacchi char vendor[] = "GENERIC";
52d65b419eSXinChen
53d65b419eSXinChen int
vendorvrfy(struct devicelist * dvp)540b4d6575SRobert Mustacchi vendorvrfy(struct devicelist *dvp)
55d65b419eSXinChen {
560b4d6575SRobert Mustacchi libscsi_hdl_t *hdl = NULL;
570b4d6575SRobert Mustacchi libscsi_target_t *targ = NULL;
580b4d6575SRobert Mustacchi libscsi_action_t *act = NULL;
590b4d6575SRobert Mustacchi libscsi_errno_t serr;
600b4d6575SRobert Mustacchi spc3_read_buffer_cdb_t *rb_cdb;
610b4d6575SRobert Mustacchi uint8_t descbuf[4];
620b4d6575SRobert Mustacchi uint32_t size;
630b4d6575SRobert Mustacchi
640b4d6575SRobert Mustacchi int ret = FWFLASH_FAILURE;
650b4d6575SRobert Mustacchi
660b4d6575SRobert Mustacchi if ((hdl = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
670b4d6575SRobert Mustacchi logmsg(MSG_ERROR, gettext("%s: failed to initialize "
680b4d6575SRobert Mustacchi "libscsi: %s\n"),
690b4d6575SRobert Mustacchi verifier->vendor, libscsi_strerror(serr));
70d65b419eSXinChen return (FWFLASH_FAILURE);
71d65b419eSXinChen }
72d65b419eSXinChen
730b4d6575SRobert Mustacchi if ((targ = libscsi_open(hdl, NULL, dvp->access_devname)) ==
740b4d6575SRobert Mustacchi NULL) {
750b4d6575SRobert Mustacchi logmsg(MSG_ERROR,
760b4d6575SRobert Mustacchi gettext("%s: unable to open device %s\n"),
770b4d6575SRobert Mustacchi verifier->vendor, dvp->access_devname);
780b4d6575SRobert Mustacchi goto cleanup;
790b4d6575SRobert Mustacchi }
80d65b419eSXinChen
810b4d6575SRobert Mustacchi if ((act = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER,
820b4d6575SRobert Mustacchi LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) {
830b4d6575SRobert Mustacchi logmsg(MSG_ERROR, "%s: failed to alloc scsi action: %s\n",
840b4d6575SRobert Mustacchi verifier->vendor, libscsi_errmsg(hdl));
850b4d6575SRobert Mustacchi goto cleanup;
860b4d6575SRobert Mustacchi }
870b4d6575SRobert Mustacchi
880b4d6575SRobert Mustacchi rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(act);
890b4d6575SRobert Mustacchi
900b4d6575SRobert Mustacchi rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR;
910b4d6575SRobert Mustacchi
920b4d6575SRobert Mustacchi /*
930b4d6575SRobert Mustacchi * Microcode upgrade usually only uses the first buffer ID which are
940b4d6575SRobert Mustacchi * sequentially indexed from zero. Strictly speaking these are all
950b4d6575SRobert Mustacchi * vendor defined, but so far most vendors we've seen use index zero
960b4d6575SRobert Mustacchi * for this.
970b4d6575SRobert Mustacchi */
980b4d6575SRobert Mustacchi rb_cdb->rbc_bufferid = 0;
990b4d6575SRobert Mustacchi
1000b4d6575SRobert Mustacchi rb_cdb->rbc_allocation_len[0] = 0;
1010b4d6575SRobert Mustacchi rb_cdb->rbc_allocation_len[1] = 0;
1020b4d6575SRobert Mustacchi rb_cdb->rbc_allocation_len[2] = sizeof (descbuf);
1030b4d6575SRobert Mustacchi
1040b4d6575SRobert Mustacchi if (libscsi_exec(act, targ) != 0) {
1050b4d6575SRobert Mustacchi logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer "
1060b4d6575SRobert Mustacchi "descriptor read: %s\n"), verifier->vendor,
1070b4d6575SRobert Mustacchi libscsi_errmsg(hdl));
1080b4d6575SRobert Mustacchi goto cleanup;
1090b4d6575SRobert Mustacchi }
1100b4d6575SRobert Mustacchi
1110b4d6575SRobert Mustacchi if (libscsi_action_get_status(act) != SAM4_STATUS_GOOD) {
1120b4d6575SRobert Mustacchi logmsg(MSG_ERROR, gettext("%s: SCSI READ BUFFER command to "
1130b4d6575SRobert Mustacchi "determine maximum image size failed\n"), verifier->vendor);
1140b4d6575SRobert Mustacchi goto cleanup;
1150b4d6575SRobert Mustacchi }
1160b4d6575SRobert Mustacchi
1170b4d6575SRobert Mustacchi if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 &&
1180b4d6575SRobert Mustacchi descbuf[3] == 0) {
1190b4d6575SRobert Mustacchi logmsg(MSG_ERROR, gettext("%s: devices %s does not support "
1200b4d6575SRobert Mustacchi "firmware upgrade\n"), verifier->vendor,
1210b4d6575SRobert Mustacchi dvp->access_devname);
1220b4d6575SRobert Mustacchi goto cleanup;
1230b4d6575SRobert Mustacchi }
1240b4d6575SRobert Mustacchi
1250b4d6575SRobert Mustacchi size = (descbuf[1] << 16) | (descbuf[2] << 8) | descbuf[3];
1260b4d6575SRobert Mustacchi logmsg(MSG_INFO, gettext("%s: checking maximum image size %u against "
1270b4d6575SRobert Mustacchi "actual image size: %u\n"), verifier->vendor, size,
1280b4d6575SRobert Mustacchi verifier->imgsize);
1290b4d6575SRobert Mustacchi if (size < verifier->imgsize) {
1300b4d6575SRobert Mustacchi logmsg(MSG_ERROR, gettext("%s: supplied firmware image %s "
1310b4d6575SRobert Mustacchi "exceeds maximum image size of %u\n"),
1320b4d6575SRobert Mustacchi verifier->vendor, verifier->imgfile, size);
1330b4d6575SRobert Mustacchi goto cleanup;
1340b4d6575SRobert Mustacchi }
1350b4d6575SRobert Mustacchi
1360b4d6575SRobert Mustacchi logmsg(MSG_INFO, gettext("%s: successfully validated images %s\n"),
1370b4d6575SRobert Mustacchi verifier->vendor, verifier->imgfile);
1380b4d6575SRobert Mustacchi
1390b4d6575SRobert Mustacchi verifier->flashbuf = 0;
1400b4d6575SRobert Mustacchi ret = FWFLASH_SUCCESS;
1410b4d6575SRobert Mustacchi cleanup:
1420b4d6575SRobert Mustacchi if (act != NULL)
1430b4d6575SRobert Mustacchi libscsi_action_free(act);
1440b4d6575SRobert Mustacchi if (targ != NULL)
1450b4d6575SRobert Mustacchi libscsi_close(hdl, targ);
1460b4d6575SRobert Mustacchi if (hdl != NULL)
1470b4d6575SRobert Mustacchi libscsi_fini(hdl);
1480b4d6575SRobert Mustacchi
1490b4d6575SRobert Mustacchi return (ret);
150d65b419eSXinChen }
151