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