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(7D).
19 *
20 * The sd(7D) 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
vendorvrfy(struct devicelist * dvp)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