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: stable/11/sbin/camcontrol/attrib.c 352289 2019-09-13 15:13:21Z mav $"); 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, 109314220Sken int task_attr, int retry_count, int timeout, int verbosemode, 110314220Sken int err_recover) 111284192Sken{ 112284192Sken union ccb *ccb = NULL; 113284192Sken int attr_num = -1; 114284192Sken#if 0 115284192Sken int num_attrs = 0; 116284192Sken#endif 117284192Sken int start_attr = 0; 118284192Sken int cached_attr = 0; 119284192Sken int read_service_action = -1; 120284192Sken int read_attr = 0, write_attr = 0; 121284192Sken int element_address = 0; 122284192Sken int element_type = ELEMENT_TYPE_ALL; 123284192Sken int partition = 0; 124284192Sken int logical_volume = 0; 125284192Sken char *endptr; 126284192Sken uint8_t *data_buf = NULL; 127284192Sken uint32_t dxfer_len = UINT16_MAX - 1; 128284192Sken uint32_t valid_len; 129284192Sken uint32_t output_format; 130284192Sken STAILQ_HEAD(, scsi_attr_desc) write_attr_list; 131284192Sken int error = 0; 132284192Sken int c; 133284192Sken 134284192Sken ccb = cam_getccb(device); 135284192Sken if (ccb == NULL) { 136284192Sken warnx("%s: error allocating CCB", __func__); 137284192Sken error = 1; 138284192Sken goto bailout; 139284192Sken } 140284192Sken 141300547Struckman CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); 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, 321314220Sken /*tag_action*/ task_attr, 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 error = 1; 348284192Sken goto bailout; 349284192Sken } 350284192Sken 351284192Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 352284192Sken if (verbosemode != 0) { 353284192Sken cam_error_print(device, ccb, CAM_ESF_ALL, 354284192Sken CAM_EPF_ALL, stderr); 355284192Sken } 356284192Sken error = 1; 357284192Sken goto bailout; 358284192Sken } 359284192Sken 360284192Sken if (read_attr == 0) 361284192Sken goto bailout; 362284192Sken 363284192Sken valid_len = dxfer_len - ccb->csio.resid; 364284192Sken 365284192Sken switch (read_service_action) { 366284192Sken case SRA_SA_ATTR_VALUES: { 367284192Sken uint32_t len_left, hdr_len, cur_len; 368284192Sken struct scsi_read_attribute_values *hdr; 369284192Sken struct scsi_mam_attribute_header *cur_id; 370284192Sken char error_str[512]; 371284192Sken uint8_t *cur_pos; 372284192Sken struct sbuf *sb; 373284192Sken 374284192Sken hdr = (struct scsi_read_attribute_values *)data_buf; 375284192Sken 376284192Sken if (valid_len < sizeof(*hdr)) { 377284192Sken fprintf(stdout, "No attributes returned.\n"); 378284192Sken error = 0; 379284192Sken goto bailout; 380284192Sken } 381284192Sken 382284192Sken sb = sbuf_new_auto(); 383284192Sken if (sb == NULL) { 384284192Sken warn("%s: Unable to allocate sbuf", __func__); 385284192Sken error = 1; 386284192Sken goto bailout; 387284192Sken } 388284192Sken /* 389284192Sken * XXX KDM grab more data if it is available. 390284192Sken */ 391284192Sken hdr_len = scsi_4btoul(hdr->length); 392284192Sken 393284192Sken for (len_left = MIN(valid_len, hdr_len), 394284192Sken cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id); 395284192Sken len_left -= cur_len, cur_pos += cur_len) { 396284192Sken int cur_attr_num; 397284192Sken cur_id = (struct scsi_mam_attribute_header *)cur_pos; 398284192Sken cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id); 399284192Sken cur_attr_num = scsi_2btoul(cur_id->id); 400284192Sken 401284192Sken if ((attr_num != -1) 402284192Sken && (cur_attr_num != attr_num)) 403284192Sken continue; 404284192Sken 405284192Sken error = scsi_attrib_sbuf(sb, cur_id, len_left, 406284192Sken /*user_table*/ NULL, /*num_user_entries*/ 0, 407284192Sken /*prefer_user_table*/ 0, output_format, error_str, 408284192Sken sizeof(error_str)); 409284192Sken if (error != 0) { 410284192Sken warnx("%s: %s", __func__, error_str); 411284192Sken sbuf_delete(sb); 412284192Sken error = 1; 413284192Sken goto bailout; 414284192Sken } 415284192Sken if (attr_num != -1) 416284192Sken break; 417284192Sken } 418284192Sken 419284192Sken sbuf_finish(sb); 420284192Sken fprintf(stdout, "%s", sbuf_data(sb)); 421284192Sken sbuf_delete(sb); 422284192Sken break; 423284192Sken } 424284192Sken case SRA_SA_SUPPORTED_ATTRS: 425284192Sken case SRA_SA_ATTR_LIST: { 426284192Sken uint32_t len_left, hdr_len; 427284192Sken struct scsi_attrib_list_header *hdr; 428284192Sken struct scsi_attrib_table_entry *entry = NULL; 429284192Sken const char *sa_name = "Supported Attributes"; 430284192Sken const char *at_name = "Available Attributes"; 431284192Sken int attr_id; 432284192Sken uint8_t *cur_id; 433284192Sken 434284192Sken hdr = (struct scsi_attrib_list_header *)data_buf; 435284192Sken if (valid_len < sizeof(*hdr)) { 436284192Sken fprintf(stdout, "No %s\n", 437284192Sken (read_service_action == SRA_SA_SUPPORTED_ATTRS)? 438284192Sken sa_name : at_name); 439284192Sken error = 0; 440284192Sken goto bailout; 441284192Sken } 442284192Sken fprintf(stdout, "%s:\n", 443284192Sken (read_service_action == SRA_SA_SUPPORTED_ATTRS) ? 444284192Sken sa_name : at_name); 445284192Sken hdr_len = scsi_4btoul(hdr->length); 446284192Sken for (len_left = MIN(valid_len, hdr_len), 447284192Sken cur_id = &hdr->first_attr_0[0]; len_left > 1; 448284192Sken len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) { 449284192Sken attr_id = scsi_2btoul(cur_id); 450284192Sken 451284192Sken if ((attr_num != -1) 452284192Sken && (attr_id != attr_num)) 453284192Sken continue; 454284192Sken 455284192Sken entry = scsi_get_attrib_entry(attr_id); 456284192Sken fprintf(stdout, "0x%.4x", attr_id); 457284192Sken if (entry == NULL) 458284192Sken fprintf(stdout, "\n"); 459284192Sken else 460284192Sken fprintf(stdout, ": %s\n", entry->desc); 461284192Sken 462284192Sken if (attr_num != -1) 463284192Sken break; 464284192Sken } 465284192Sken break; 466284192Sken } 467284192Sken case SRA_SA_PART_LIST: 468284192Sken case SRA_SA_LOG_VOL_LIST: { 469284192Sken struct scsi_attrib_lv_list *lv_list; 470284192Sken const char *partition_name = "Partition"; 471284192Sken const char *lv_name = "Logical Volume"; 472284192Sken 473284192Sken if (valid_len < sizeof(*lv_list)) { 474284192Sken fprintf(stdout, "No %s list returned\n", 475284192Sken (read_service_action == SRA_SA_PART_LIST) ? 476284192Sken partition_name : lv_name); 477284192Sken error = 0; 478284192Sken goto bailout; 479284192Sken } 480284192Sken 481284192Sken lv_list = (struct scsi_attrib_lv_list *)data_buf; 482284192Sken 483284192Sken fprintf(stdout, "First %s: %d\n", 484284192Sken (read_service_action == SRA_SA_PART_LIST) ? 485284192Sken partition_name : lv_name, 486284192Sken lv_list->first_lv_number); 487284192Sken fprintf(stdout, "Number of %ss: %d\n", 488284192Sken (read_service_action == SRA_SA_PART_LIST) ? 489284192Sken partition_name : lv_name, 490284192Sken lv_list->num_logical_volumes); 491284192Sken break; 492284192Sken } 493284192Sken default: 494284192Sken break; 495284192Sken } 496284192Skenbailout: 497284192Sken if (ccb != NULL) 498284192Sken cam_freeccb(ccb); 499284192Sken 500284192Sken free(data_buf); 501284192Sken 502284192Sken return (error); 503284192Sken} 504