1284192Sken/*-
2284192Sken * Copyright (c) 2014 Spectra Logic Corporation
3284192Sken * All rights reserved.
4284192Sken *
5284192Sken * Redistribution and use in source and binary forms, with or without
6284192Sken * modification, are permitted provided that the following conditions
7284192Sken * are met:
8284192Sken * 1. Redistributions of source code must retain the above copyright
9284192Sken *    notice, this list of conditions, and the following disclaimer,
10284192Sken *    without modification.
11284192Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12284192Sken *    substantially similar to the "NO WARRANTY" disclaimer below
13284192Sken *    ("Disclaimer") and any redistribution must be conditioned upon
14284192Sken *    including a substantially similar Disclaimer requirement for further
15284192Sken *    binary redistribution.
16284192Sken *
17284192Sken * NO WARRANTY
18284192Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19284192Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20284192Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21284192Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22284192Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23284192Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24284192Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25284192Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26284192Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27284192Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28284192Sken * POSSIBILITY OF SUCH DAMAGES.
29284192Sken *
30284192Sken * Authors: Ken Merry           (Spectra Logic Corporation)
31284192Sken */
32284192Sken/*
33284192Sken * SCSI Read and Write Attribute support for camcontrol(8).
34284192Sken */
35284192Sken
36284192Sken#include <sys/cdefs.h>
37284192Sken__FBSDID("$FreeBSD: releng/10.3/sbin/camcontrol/attrib.c 284435 2015-06-16 02:31:11Z ken $");
38284192Sken
39284192Sken#include <sys/ioctl.h>
40284192Sken#include <sys/stdint.h>
41284192Sken#include <sys/types.h>
42284192Sken#include <sys/endian.h>
43284192Sken#include <sys/sbuf.h>
44284192Sken#include <sys/queue.h>
45284192Sken#include <sys/chio.h>
46284192Sken
47284192Sken#include <stdio.h>
48284192Sken#include <stdlib.h>
49284192Sken#include <inttypes.h>
50284192Sken#include <unistd.h>
51284192Sken#include <string.h>
52284192Sken#include <strings.h>
53284192Sken#include <fcntl.h>
54284192Sken#include <ctype.h>
55284192Sken#include <limits.h>
56284192Sken#include <err.h>
57284192Sken#include <locale.h>
58284192Sken
59284192Sken#include <cam/cam.h>
60284192Sken#include <cam/cam_debug.h>
61284192Sken#include <cam/cam_ccb.h>
62284192Sken#include <cam/scsi/scsi_all.h>
63284192Sken#include <cam/scsi/scsi_pass.h>
64284192Sken#include <cam/scsi/scsi_ch.h>
65284192Sken#include <cam/scsi/scsi_message.h>
66284192Sken#include <camlib.h>
67284192Sken#include "camcontrol.h"
68284192Sken
69284192Sken#if 0
70284192Skenstruct scsi_attr_desc {
71284192Sken	int attr_id;
72284192Sken
73284192Sken	STAILQ_ENTRY(scsi_attr_desc) links;
74284192Sken};
75284192Sken#endif
76284192Sken
77284192Skenstatic struct scsi_nv elem_type_map[] = {
78284192Sken	{ "all", ELEMENT_TYPE_ALL },
79284192Sken	{ "picker", ELEMENT_TYPE_MT },
80284192Sken	{ "slot", ELEMENT_TYPE_ST },
81284192Sken	{ "portal", ELEMENT_TYPE_IE },
82284192Sken	{ "drive", ELEMENT_TYPE_DT },
83284192Sken};
84284192Sken
85284192Skenstatic struct scsi_nv sa_map[] = {
86284192Sken	{ "attr_values", SRA_SA_ATTR_VALUES },
87284192Sken	{ "attr_list", SRA_SA_ATTR_LIST },
88284192Sken	{ "lv_list", SRA_SA_LOG_VOL_LIST },
89284192Sken	{ "part_list", SRA_SA_PART_LIST },
90284192Sken	{ "supp_attr", SRA_SA_SUPPORTED_ATTRS }
91284192Sken};
92284192Sken
93284192Skenstatic struct scsi_nv output_format_map[] = {
94284192Sken	{ "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
95284192Sken	{ "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
96284192Sken	{ "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
97284192Sken	{ "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
98284192Sken	{ "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
99284192Sken	{ "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
100284192Sken	{ "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
101284192Sken	{ "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
102284192Sken	{ "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
103284192Sken	{ "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
104284192Sken	{ "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
105284192Sken};
106284192Sken
107284192Skenint
108284192Skenscsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
109284192Sken	   int retry_count, int timeout, int verbosemode, int err_recover)
110284192Sken{
111284192Sken	union ccb *ccb = NULL;
112284192Sken	int attr_num = -1;
113284192Sken#if 0
114284192Sken	int num_attrs = 0;
115284192Sken#endif
116284192Sken	int start_attr = 0;
117284192Sken	int cached_attr = 0;
118284192Sken	int read_service_action = -1;
119284192Sken	int read_attr = 0, write_attr = 0;
120284192Sken	int element_address = 0;
121284192Sken	int element_type = ELEMENT_TYPE_ALL;
122284192Sken	int partition = 0;
123284192Sken	int logical_volume = 0;
124284192Sken	char *endptr;
125284192Sken	uint8_t *data_buf = NULL;
126284192Sken	uint32_t dxfer_len = UINT16_MAX - 1;
127284192Sken	uint32_t valid_len;
128284192Sken	uint32_t output_format;
129284192Sken	STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
130284192Sken	int error = 0;
131284192Sken	int c;
132284192Sken
133284192Sken	ccb = cam_getccb(device);
134284192Sken	if (ccb == NULL) {
135284192Sken		warnx("%s: error allocating CCB", __func__);
136284192Sken		error = 1;
137284192Sken		goto bailout;
138284192Sken	}
139284192Sken
140284192Sken	bzero(&(&ccb->ccb_h)[1],
141284192Sken	      sizeof(union ccb) - sizeof(struct ccb_hdr));
142284192Sken
143284192Sken	STAILQ_INIT(&write_attr_list);
144284192Sken
145284192Sken	/*
146284192Sken	 * By default, when displaying attribute values, we trim out
147284192Sken	 * non-ASCII characters in ASCII fields.  We display all fields
148284192Sken	 * (description, attribute number, attribute size, and readonly
149284192Sken	 * status).  We default to displaying raw text.
150284192Sken	 *
151284192Sken	 * XXX KDM need to port this to stable/10 and newer FreeBSD
152284192Sken	 * versions that have iconv built in and can convert codesets.
153284192Sken	 */
154284192Sken	output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
155284192Sken			SCSI_ATTR_OUTPUT_FIELD_ALL |
156284192Sken			SCSI_ATTR_OUTPUT_TEXT_RAW;
157284192Sken
158284192Sken	data_buf = malloc(dxfer_len);
159284192Sken	if (data_buf == NULL) {
160284192Sken		warn("%s: error allocating %u bytes", __func__, dxfer_len);
161284192Sken		error = 1;
162284192Sken		goto bailout;
163284192Sken	}
164284192Sken
165284192Sken	while ((c = getopt(argc, argv, combinedopt)) != -1) {
166284192Sken		switch (c) {
167284192Sken		case 'a':
168284192Sken			attr_num = strtol(optarg, &endptr, 0);
169284192Sken			if (*endptr != '\0') {
170284192Sken				warnx("%s: invalid attribute number %s",
171284192Sken				    __func__, optarg);
172284192Sken				error = 1;
173284192Sken				goto bailout;
174284192Sken			}
175284192Sken			start_attr = attr_num;
176284192Sken			break;
177284192Sken		case 'c':
178284192Sken			cached_attr = 1;
179284192Sken			break;
180284192Sken		case 'e':
181284192Sken			element_address = strtol(optarg, &endptr, 0);
182284192Sken			if (*endptr != '\0') {
183284192Sken				warnx("%s: invalid element address %s",
184284192Sken				    __func__, optarg);
185284192Sken				error = 1;
186284192Sken				goto bailout;
187284192Sken			}
188284192Sken			break;
189284192Sken		case 'F': {
190284192Sken			scsi_nv_status status;
191284192Sken			scsi_attrib_output_flags new_outflags;
192284192Sken			int entry_num = 0;
193284192Sken			char *tmpstr;
194284192Sken
195284192Sken			if (isdigit(optarg[0])) {
196284192Sken				output_format = strtoul(optarg, &endptr, 0);
197284192Sken				if (*endptr != '\0') {
198284192Sken					warnx("%s: invalid numeric output "
199284192Sken					    "format argument %s", __func__,
200284192Sken					    optarg);
201284192Sken					error = 1;
202284192Sken					goto bailout;
203284192Sken				}
204284192Sken				break;
205284192Sken			}
206284192Sken			new_outflags = SCSI_ATTR_OUTPUT_NONE;
207284192Sken
208284192Sken			while ((tmpstr = strsep(&optarg, ",")) != NULL) {
209284192Sken				status = scsi_get_nv(output_format_map,
210284192Sken				    sizeof(output_format_map) /
211284192Sken				    sizeof(output_format_map[0]), tmpstr,
212284192Sken				    &entry_num, SCSI_NV_FLAG_IG_CASE);
213284192Sken
214284192Sken				if (status == SCSI_NV_FOUND)
215284192Sken					new_outflags |=
216284192Sken					    output_format_map[entry_num].value;
217284192Sken				else {
218284192Sken					warnx("%s: %s format option %s",
219284192Sken					    __func__,
220284192Sken					    (status == SCSI_NV_AMBIGUOUS) ?
221284192Sken					    "ambiguous" : "invalid", tmpstr);
222284192Sken					error = 1;
223284192Sken					goto bailout;
224284192Sken				}
225284192Sken			}
226284192Sken			output_format = new_outflags;
227284192Sken			break;
228284192Sken		}
229284192Sken		case 'p':
230284192Sken			partition = strtol(optarg, &endptr, 0);
231284192Sken			if (*endptr != '\0') {
232284192Sken				warnx("%s: invalid partition number %s",
233284192Sken				    __func__, optarg);
234284192Sken				error = 1;
235284192Sken				goto bailout;
236284192Sken			}
237284192Sken			break;
238284192Sken		case 'r': {
239284192Sken			scsi_nv_status status;
240284192Sken			int entry_num = 0;
241284192Sken
242284192Sken			status = scsi_get_nv(sa_map, sizeof(sa_map) /
243284192Sken			    sizeof(sa_map[0]), optarg, &entry_num,
244284192Sken			    SCSI_NV_FLAG_IG_CASE);
245284192Sken			if (status == SCSI_NV_FOUND)
246284192Sken				read_service_action = sa_map[entry_num].value;
247284192Sken			else {
248284192Sken				warnx("%s: %s %s option %s", __func__,
249284192Sken				    (status == SCSI_NV_AMBIGUOUS) ?
250284192Sken				    "ambiguous" : "invalid", "service action",
251284192Sken				    optarg);
252284192Sken				error = 1;
253284192Sken				goto bailout;
254284192Sken			}
255284192Sken			read_attr = 1;
256284192Sken			break;
257284192Sken		}
258284192Sken		case 's':
259284192Sken			start_attr = strtol(optarg, &endptr, 0);
260284192Sken			if (*endptr != '\0') {
261284192Sken				warnx("%s: invalid starting attr argument %s",
262284192Sken				    __func__, optarg);
263284192Sken				error = 1;
264284192Sken				goto bailout;
265284192Sken			}
266284192Sken			break;
267284192Sken		case 'T': {
268284192Sken			scsi_nv_status status;
269284192Sken			int entry_num = 0;
270284192Sken
271284192Sken			status = scsi_get_nv(elem_type_map,
272284192Sken			    sizeof(elem_type_map) / sizeof(elem_type_map[0]),
273284192Sken			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
274284192Sken			if (status == SCSI_NV_FOUND)
275284192Sken				element_type = elem_type_map[entry_num].value;
276284192Sken			else {
277284192Sken				warnx("%s: %s %s option %s", __func__,
278284192Sken				    (status == SCSI_NV_AMBIGUOUS) ?
279284192Sken				    "ambiguous" : "invalid", "element type",
280284192Sken				    optarg);
281284192Sken				error = 1;
282284192Sken				goto bailout;
283284192Sken			}
284284192Sken			break;
285284192Sken		}
286284192Sken		case 'w':
287284192Sken			warnx("%s: writing attributes is not implemented yet",
288284192Sken			      __func__);
289284192Sken			error = 1;
290284192Sken			goto bailout;
291284192Sken			break;
292284192Sken		case 'V':
293284192Sken			logical_volume = strtol(optarg, &endptr, 0);
294284192Sken
295284192Sken			if (*endptr != '\0') {
296284192Sken				warnx("%s: invalid logical volume argument %s",
297284192Sken				    __func__, optarg);
298284192Sken				error = 1;
299284192Sken				goto bailout;
300284192Sken			}
301284192Sken			break;
302284192Sken		default:
303284192Sken			break;
304284192Sken		}
305284192Sken	}
306284192Sken
307284192Sken	/*
308284192Sken	 * Default to reading attributes
309284192Sken	 */
310284192Sken	if (((read_attr == 0) && (write_attr == 0))
311284192Sken	 || ((read_attr != 0) && (write_attr != 0))) {
312284192Sken		warnx("%s: Must specify either -r or -w", __func__);
313284192Sken		error = 1;
314284192Sken		goto bailout;
315284192Sken	}
316284192Sken
317284192Sken	if (read_attr != 0) {
318284192Sken		scsi_read_attribute(&ccb->csio,
319284192Sken				    /*retries*/ retry_count,
320284192Sken				    /*cbfcnp*/ NULL,
321284192Sken				    /*tag_action*/ MSG_SIMPLE_Q_TAG,
322284192Sken				    /*service_action*/ read_service_action,
323284192Sken				    /*element*/ element_address,
324284192Sken				    /*elem_type*/ element_type,
325284192Sken				    /*logical_volume*/ logical_volume,
326284192Sken				    /*partition*/ partition,
327284192Sken				    /*first_attribute*/ start_attr,
328284192Sken				    /*cache*/ cached_attr,
329284192Sken				    /*data_ptr*/ data_buf,
330284192Sken				    /*length*/ dxfer_len,
331284192Sken			            /*sense_len*/ SSD_FULL_SIZE,
332284192Sken				    /*timeout*/ timeout ? timeout : 60000);
333284192Sken#if 0
334284192Sken	} else {
335284192Sken#endif
336284192Sken
337284192Sken	}
338284192Sken
339284192Sken	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
340284192Sken
341284192Sken	if (err_recover != 0)
342284192Sken		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
343284192Sken
344284192Sken	if (cam_send_ccb(device, ccb) < 0) {
345284192Sken		warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
346284192Sken		    "READ" : "WRITE");
347284192Sken
348284192Sken		if (verbosemode != 0) {
349284192Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
350284192Sken					CAM_EPF_ALL, stderr);
351284192Sken		}
352284192Sken
353284192Sken		error = 1;
354284192Sken		goto bailout;
355284192Sken	}
356284192Sken
357284192Sken	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
358284192Sken		if (verbosemode != 0) {
359284192Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
360284192Sken					CAM_EPF_ALL, stderr);
361284192Sken		}
362284192Sken		error = 1;
363284192Sken		goto bailout;
364284192Sken	}
365284192Sken
366284192Sken	if (read_attr == 0)
367284192Sken		goto bailout;
368284192Sken
369284192Sken	valid_len = dxfer_len - ccb->csio.resid;
370284192Sken
371284192Sken	switch (read_service_action) {
372284192Sken	case SRA_SA_ATTR_VALUES: {
373284192Sken		uint32_t len_left, hdr_len, cur_len;
374284192Sken		struct scsi_read_attribute_values *hdr;
375284192Sken		struct scsi_mam_attribute_header *cur_id;
376284192Sken		char error_str[512];
377284192Sken		uint8_t *cur_pos;
378284192Sken		struct sbuf *sb;
379284192Sken
380284192Sken		hdr = (struct scsi_read_attribute_values *)data_buf;
381284192Sken
382284192Sken		if (valid_len < sizeof(*hdr)) {
383284192Sken			fprintf(stdout, "No attributes returned.\n");
384284192Sken			error = 0;
385284192Sken			goto bailout;
386284192Sken		}
387284192Sken
388284192Sken		sb = sbuf_new_auto();
389284192Sken		if (sb == NULL) {
390284192Sken			warn("%s: Unable to allocate sbuf", __func__);
391284192Sken			error = 1;
392284192Sken			goto bailout;
393284192Sken		}
394284192Sken		/*
395284192Sken		 * XXX KDM grab more data if it is available.
396284192Sken		 */
397284192Sken		hdr_len = scsi_4btoul(hdr->length);
398284192Sken
399284192Sken		for (len_left = MIN(valid_len, hdr_len),
400284192Sken		     cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
401284192Sken		     len_left -= cur_len, cur_pos += cur_len) {
402284192Sken			int cur_attr_num;
403284192Sken			cur_id = (struct scsi_mam_attribute_header *)cur_pos;
404284192Sken			cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
405284192Sken			cur_attr_num = scsi_2btoul(cur_id->id);
406284192Sken
407284192Sken			if ((attr_num != -1)
408284192Sken			 && (cur_attr_num != attr_num))
409284192Sken				continue;
410284192Sken
411284192Sken			error = scsi_attrib_sbuf(sb, cur_id, len_left,
412284192Sken			    /*user_table*/ NULL, /*num_user_entries*/ 0,
413284192Sken			    /*prefer_user_table*/ 0, output_format, error_str,
414284192Sken			    sizeof(error_str));
415284192Sken			if (error != 0) {
416284192Sken				warnx("%s: %s", __func__, error_str);
417284192Sken				sbuf_delete(sb);
418284192Sken				error = 1;
419284192Sken				goto bailout;
420284192Sken			}
421284192Sken			if (attr_num != -1)
422284192Sken				break;
423284192Sken		}
424284192Sken
425284192Sken		sbuf_finish(sb);
426284192Sken		fprintf(stdout, "%s", sbuf_data(sb));
427284192Sken		sbuf_delete(sb);
428284192Sken		break;
429284192Sken	}
430284192Sken	case SRA_SA_SUPPORTED_ATTRS:
431284192Sken	case SRA_SA_ATTR_LIST: {
432284192Sken		uint32_t len_left, hdr_len;
433284192Sken		struct scsi_attrib_list_header *hdr;
434284192Sken		struct scsi_attrib_table_entry *entry = NULL;
435284192Sken		const char *sa_name = "Supported Attributes";
436284192Sken		const char *at_name = "Available Attributes";
437284192Sken		int attr_id;
438284192Sken		uint8_t *cur_id;
439284192Sken
440284192Sken		hdr = (struct scsi_attrib_list_header *)data_buf;
441284192Sken		if (valid_len < sizeof(*hdr)) {
442284192Sken			fprintf(stdout, "No %s\n",
443284192Sken				(read_service_action == SRA_SA_SUPPORTED_ATTRS)?
444284192Sken				 sa_name : at_name);
445284192Sken			error = 0;
446284192Sken			goto bailout;
447284192Sken		}
448284192Sken		fprintf(stdout, "%s:\n",
449284192Sken			(read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
450284192Sken			 sa_name : at_name);
451284192Sken		hdr_len = scsi_4btoul(hdr->length);
452284192Sken		for (len_left = MIN(valid_len, hdr_len),
453284192Sken		     cur_id = &hdr->first_attr_0[0]; len_left > 1;
454284192Sken		     len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
455284192Sken			attr_id = scsi_2btoul(cur_id);
456284192Sken
457284192Sken			if ((attr_num != -1)
458284192Sken			 && (attr_id != attr_num))
459284192Sken				continue;
460284192Sken
461284192Sken			entry = scsi_get_attrib_entry(attr_id);
462284192Sken			fprintf(stdout, "0x%.4x", attr_id);
463284192Sken			if (entry == NULL)
464284192Sken				fprintf(stdout, "\n");
465284192Sken			else
466284192Sken				fprintf(stdout, ": %s\n", entry->desc);
467284192Sken
468284192Sken			if (attr_num != -1)
469284192Sken				break;
470284192Sken		}
471284192Sken		break;
472284192Sken	}
473284192Sken	case SRA_SA_PART_LIST:
474284192Sken	case SRA_SA_LOG_VOL_LIST: {
475284192Sken		struct scsi_attrib_lv_list *lv_list;
476284192Sken		const char *partition_name = "Partition";
477284192Sken		const char *lv_name = "Logical Volume";
478284192Sken
479284192Sken		if (valid_len < sizeof(*lv_list)) {
480284192Sken			fprintf(stdout, "No %s list returned\n",
481284192Sken				(read_service_action == SRA_SA_PART_LIST) ?
482284192Sken				partition_name : lv_name);
483284192Sken			error = 0;
484284192Sken			goto bailout;
485284192Sken		}
486284192Sken
487284192Sken		lv_list = (struct scsi_attrib_lv_list *)data_buf;
488284192Sken
489284192Sken		fprintf(stdout, "First %s: %d\n",
490284192Sken			(read_service_action == SRA_SA_PART_LIST) ?
491284192Sken			partition_name : lv_name,
492284192Sken			lv_list->first_lv_number);
493284192Sken		fprintf(stdout, "Number of %ss: %d\n",
494284192Sken			(read_service_action == SRA_SA_PART_LIST) ?
495284192Sken			partition_name : lv_name,
496284192Sken			lv_list->num_logical_volumes);
497284192Sken		break;
498284192Sken	}
499284192Sken	default:
500284192Sken		break;
501284192Sken	}
502284192Skenbailout:
503284192Sken	if (ccb != NULL)
504284192Sken		cam_freeccb(ccb);
505284192Sken
506284192Sken	free(data_buf);
507284192Sken
508284192Sken	return (error);
509284192Sken}
510