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