diff --git a/sbin/camcontrol/attrib.c b/sbin/camcontrol/attrib.c index 7e89dbc3d48e..4cf9832756b4 100644 --- a/sbin/camcontrol/attrib.c +++ b/sbin/camcontrol/attrib.c @@ -1,499 +1,499 @@ /*- * Copyright (c) 2014 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * Authors: Ken Merry (Spectra Logic Corporation) */ /* * SCSI Read and Write Attribute support for camcontrol(8). */ #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camcontrol.h" #if 0 struct scsi_attr_desc { int attr_id; STAILQ_ENTRY(scsi_attr_desc) links; }; #endif static struct scsi_nv elem_type_map[] = { { "all", ELEMENT_TYPE_ALL }, { "picker", ELEMENT_TYPE_MT }, { "slot", ELEMENT_TYPE_ST }, { "portal", ELEMENT_TYPE_IE }, { "drive", ELEMENT_TYPE_DT }, }; static struct scsi_nv sa_map[] = { { "attr_values", SRA_SA_ATTR_VALUES }, { "attr_list", SRA_SA_ATTR_LIST }, { "lv_list", SRA_SA_LOG_VOL_LIST }, { "part_list", SRA_SA_PART_LIST }, { "supp_attr", SRA_SA_SUPPORTED_ATTRS } }; static struct scsi_nv output_format_map[] = { { "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC }, { "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW }, { "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC }, { "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM }, { "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW }, { "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL }, { "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE }, { "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC }, { "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM }, { "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE }, { "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW }, }; int scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt, int task_attr, int retry_count, int timeout, int verbosemode, int err_recover) { union ccb *ccb = NULL; int attr_num = -1; #if 0 int num_attrs = 0; #endif int start_attr = 0; int cached_attr = 0; int read_service_action = -1; int read_attr = 0, write_attr = 0; int element_address = 0; int element_type = ELEMENT_TYPE_ALL; int partition = 0; int logical_volume = 0; char *endptr; uint8_t *data_buf = NULL; uint32_t dxfer_len = UINT16_MAX - 1; uint32_t valid_len; uint32_t output_format; STAILQ_HEAD(, scsi_attr_desc) write_attr_list; int error = 0; int c; ccb = cam_getccb(device); if (ccb == NULL) { warnx("%s: error allocating CCB", __func__); error = 1; goto bailout; } STAILQ_INIT(&write_attr_list); /* * By default, when displaying attribute values, we trim out * non-ASCII characters in ASCII fields. We display all fields * (description, attribute number, attribute size, and readonly * status). We default to displaying raw text. * * XXX KDM need to port this to stable/10 and newer FreeBSD * versions that have iconv built in and can convert codesets. */ output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM | SCSI_ATTR_OUTPUT_FIELD_ALL | SCSI_ATTR_OUTPUT_TEXT_RAW; data_buf = malloc(dxfer_len); if (data_buf == NULL) { warn("%s: error allocating %u bytes", __func__, dxfer_len); error = 1; goto bailout; } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': attr_num = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid attribute number %s", __func__, optarg); error = 1; goto bailout; } start_attr = attr_num; break; case 'c': cached_attr = 1; break; case 'e': element_address = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid element address %s", __func__, optarg); error = 1; goto bailout; } break; case 'F': { scsi_nv_status status; scsi_attrib_output_flags new_outflags; int entry_num = 0; char *tmpstr; if (isdigit(optarg[0])) { output_format = strtoul(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid numeric output " "format argument %s", __func__, optarg); error = 1; goto bailout; } break; } new_outflags = SCSI_ATTR_OUTPUT_NONE; while ((tmpstr = strsep(&optarg, ",")) != NULL) { status = scsi_get_nv(output_format_map, sizeof(output_format_map) / sizeof(output_format_map[0]), tmpstr, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) new_outflags |= output_format_map[entry_num].value; else { warnx("%s: %s format option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", tmpstr); error = 1; goto bailout; } } output_format = new_outflags; break; } case 'p': partition = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid partition number %s", __func__, optarg); error = 1; goto bailout; } break; case 'r': { scsi_nv_status status; int entry_num = 0; status = scsi_get_nv(sa_map, sizeof(sa_map) / sizeof(sa_map[0]), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) read_service_action = sa_map[entry_num].value; else { warnx("%s: %s %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "service action", optarg); error = 1; goto bailout; } read_attr = 1; break; } case 's': start_attr = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid starting attr argument %s", __func__, optarg); error = 1; goto bailout; } break; case 'T': { scsi_nv_status status; int entry_num = 0; status = scsi_get_nv(elem_type_map, - sizeof(elem_type_map) / sizeof(elem_type_map[0]), + nitems(elem_type_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) element_type = elem_type_map[entry_num].value; else { warnx("%s: %s %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "element type", optarg); error = 1; goto bailout; } break; } case 'w': warnx("%s: writing attributes is not implemented yet", __func__); error = 1; goto bailout; break; case 'V': logical_volume = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid logical volume argument %s", __func__, optarg); error = 1; goto bailout; } break; default: break; } } /* * Default to reading attributes */ if (((read_attr == 0) && (write_attr == 0)) || ((read_attr != 0) && (write_attr != 0))) { warnx("%s: Must specify either -r or -w", __func__); error = 1; goto bailout; } if (read_attr != 0) { scsi_read_attribute(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*tag_action*/ task_attr, /*service_action*/ read_service_action, /*element*/ element_address, /*elem_type*/ element_type, /*logical_volume*/ logical_volume, /*partition*/ partition, /*first_attribute*/ start_attr, /*cache*/ cached_attr, /*data_ptr*/ data_buf, /*length*/ dxfer_len, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000); #if 0 } else { #endif } ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (err_recover != 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { warn("error sending %s ATTRIBUTE", (read_attr != 0) ? "READ" : "WRITE"); error = 1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (verbosemode != 0) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); } error = 1; goto bailout; } if (read_attr == 0) goto bailout; valid_len = dxfer_len - ccb->csio.resid; switch (read_service_action) { case SRA_SA_ATTR_VALUES: { uint32_t len_left, hdr_len, cur_len; struct scsi_read_attribute_values *hdr; struct scsi_mam_attribute_header *cur_id; char error_str[512]; uint8_t *cur_pos; struct sbuf *sb; hdr = (struct scsi_read_attribute_values *)data_buf; if (valid_len < sizeof(*hdr)) { fprintf(stdout, "No attributes returned.\n"); error = 0; goto bailout; } sb = sbuf_new_auto(); if (sb == NULL) { warn("%s: Unable to allocate sbuf", __func__); error = 1; goto bailout; } /* * XXX KDM grab more data if it is available. */ hdr_len = scsi_4btoul(hdr->length); for (len_left = MIN(valid_len, hdr_len), cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id); len_left -= cur_len, cur_pos += cur_len) { int cur_attr_num; cur_id = (struct scsi_mam_attribute_header *)cur_pos; cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id); cur_attr_num = scsi_2btoul(cur_id->id); if ((attr_num != -1) && (cur_attr_num != attr_num)) continue; error = scsi_attrib_sbuf(sb, cur_id, len_left, /*user_table*/ NULL, /*num_user_entries*/ 0, /*prefer_user_table*/ 0, output_format, error_str, sizeof(error_str)); if (error != 0) { warnx("%s: %s", __func__, error_str); sbuf_delete(sb); error = 1; goto bailout; } if (attr_num != -1) break; } sbuf_finish(sb); fprintf(stdout, "%s", sbuf_data(sb)); sbuf_delete(sb); break; } case SRA_SA_SUPPORTED_ATTRS: case SRA_SA_ATTR_LIST: { uint32_t len_left, hdr_len; struct scsi_attrib_list_header *hdr; struct scsi_attrib_table_entry *entry = NULL; const char *sa_name = "Supported Attributes"; const char *at_name = "Available Attributes"; int attr_id; uint8_t *cur_id; hdr = (struct scsi_attrib_list_header *)data_buf; if (valid_len < sizeof(*hdr)) { fprintf(stdout, "No %s\n", (read_service_action == SRA_SA_SUPPORTED_ATTRS)? sa_name : at_name); error = 0; goto bailout; } fprintf(stdout, "%s:\n", (read_service_action == SRA_SA_SUPPORTED_ATTRS) ? sa_name : at_name); hdr_len = scsi_4btoul(hdr->length); for (len_left = MIN(valid_len, hdr_len), cur_id = &hdr->first_attr_0[0]; len_left > 1; len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) { attr_id = scsi_2btoul(cur_id); if ((attr_num != -1) && (attr_id != attr_num)) continue; entry = scsi_get_attrib_entry(attr_id); fprintf(stdout, "0x%.4x", attr_id); if (entry == NULL) fprintf(stdout, "\n"); else fprintf(stdout, ": %s\n", entry->desc); if (attr_num != -1) break; } break; } case SRA_SA_PART_LIST: case SRA_SA_LOG_VOL_LIST: { struct scsi_attrib_lv_list *lv_list; const char *partition_name = "Partition"; const char *lv_name = "Logical Volume"; if (valid_len < sizeof(*lv_list)) { fprintf(stdout, "No %s list returned\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name); error = 0; goto bailout; } lv_list = (struct scsi_attrib_lv_list *)data_buf; fprintf(stdout, "First %s: %d\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name, lv_list->first_lv_number); fprintf(stdout, "Number of %ss: %d\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name, lv_list->num_logical_volumes); break; } default: break; } bailout: if (ccb != NULL) cam_freeccb(ccb); free(data_buf); return (error); } diff --git a/sbin/camcontrol/epc.c b/sbin/camcontrol/epc.c index 206b34594f4f..4273ad19047c 100644 --- a/sbin/camcontrol/epc.c +++ b/sbin/camcontrol/epc.c @@ -1,853 +1,852 @@ /*- * Copyright (c) 2016 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * Authors: Ken Merry (Spectra Logic Corporation) */ /* * ATA Extended Power Conditions (EPC) support */ -#include +#include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camcontrol.h" typedef enum { EPC_ACTION_NONE = 0x00, EPC_ACTION_LIST = 0x01, EPC_ACTION_TIMER_SET = 0x02, EPC_ACTION_IMMEDIATE = 0x03, EPC_ACTION_GETMODE = 0x04 } epc_action; static struct scsi_nv epc_flags[] = { { "Supported", ATA_PCL_COND_SUPPORTED }, { "Saveable", ATA_PCL_COND_SUPPORTED }, { "Changeable", ATA_PCL_COND_CHANGEABLE }, { "Default Timer Enabled", ATA_PCL_DEFAULT_TIMER_EN }, { "Saved Timer Enabled", ATA_PCL_SAVED_TIMER_EN }, { "Current Timer Enabled", ATA_PCL_CURRENT_TIMER_EN }, { "Hold Power Condition Not Supported", ATA_PCL_HOLD_PC_NOT_SUP } }; static struct scsi_nv epc_power_cond_map[] = { { "Standby_z", ATA_EPC_STANDBY_Z }, { "z", ATA_EPC_STANDBY_Z }, { "Standby_y", ATA_EPC_STANDBY_Y }, { "y", ATA_EPC_STANDBY_Y }, { "Idle_a", ATA_EPC_IDLE_A }, { "a", ATA_EPC_IDLE_A }, { "Idle_b", ATA_EPC_IDLE_B }, { "b", ATA_EPC_IDLE_B }, { "Idle_c", ATA_EPC_IDLE_C }, { "c", ATA_EPC_IDLE_C } }; static struct scsi_nv epc_rst_val[] = { { "default", ATA_SF_EPC_RST_DFLT }, { "saved", 0} }; static struct scsi_nv epc_ps_map[] = { { "unknown", ATA_SF_EPC_SRC_UNKNOWN }, { "battery", ATA_SF_EPC_SRC_BAT }, { "notbattery", ATA_SF_EPC_SRC_NOT_BAT } }; /* * These aren't subcommands of the EPC SET FEATURES subcommand, but rather * commands that determine the current capabilities and status of the drive. * The EPC subcommands are limited to 4 bits, so we won't collide with any * future values. */ #define CCTL_EPC_GET_STATUS 0x8001 #define CCTL_EPC_LIST 0x8002 static struct scsi_nv epc_cmd_map[] = { { "restore", ATA_SF_EPC_RESTORE }, { "goto", ATA_SF_EPC_GOTO }, { "timer", ATA_SF_EPC_SET_TIMER }, { "state", ATA_SF_EPC_SET_STATE }, { "enable", ATA_SF_EPC_ENABLE }, { "disable", ATA_SF_EPC_DISABLE }, { "source", ATA_SF_EPC_SET_SOURCE }, { "status", CCTL_EPC_GET_STATUS }, { "list", CCTL_EPC_LIST } }; static int epc_list(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout); static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix); static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout, int power_only); static int epc_set_features(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout, int action, int power_cond, int timer, int enable, int save, int delayed_entry, int hold, int power_src, int restore_src); static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix) { int first; unsigned int i, num_printed, max_chars; first = 1; max_chars = 75; num_printed = printf("%sFlags: ", prefix); - for (i = 0; i < (sizeof(epc_flags) / sizeof(epc_flags[0])); i++) { + for (i = 0; i < nitems(epc_flags); i++) { if ((desc->flags & epc_flags[i].value) == 0) continue; if (first == 0) { num_printed += printf(", "); } if ((num_printed + strlen(epc_flags[i].name)) > max_chars) { printf("\n"); num_printed = printf("%s ", prefix); } num_printed += printf("%s", epc_flags[i].name); first = 0; } if (first != 0) printf("None"); printf("\n"); printf("%sDefault timer setting: %.1f sec\n", prefix, (double)(le32dec(desc->default_timer) / 10)); printf("%sSaved timer setting: %.1f sec\n", prefix, (double)(le32dec(desc->saved_timer) / 10)); printf("%sCurrent timer setting: %.1f sec\n", prefix, (double)(le32dec(desc->current_timer) / 10)); printf("%sNominal time to active: %.1f sec\n", prefix, (double)(le32dec(desc->nom_time_to_active) / 10)); printf("%sMinimum timer: %.1f sec\n", prefix, (double)(le32dec(desc->min_timer) / 10)); printf("%sMaximum timer: %.1f sec\n", prefix, (double)(le32dec(desc->max_timer) / 10)); printf("%sNumber of transitions to power condition: %u\n", prefix, le32dec(desc->num_transitions_to_pc)); printf("%sHours in power condition: %u\n", prefix, le32dec(desc->hours_in_pc)); } static int epc_list(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout) { struct ata_power_cond_log_idle *idle_log; struct ata_power_cond_log_standby *standby_log; uint8_t log_buf[sizeof(*idle_log) + sizeof(*standby_log)]; uint16_t log_addr = ATA_POWER_COND_LOG; uint16_t page_number = ATA_PCL_IDLE; uint64_t lba; int error = 0; lba = (((uint64_t)page_number & 0xff00) << 32) | ((page_number & 0x00ff) << 8) | (log_addr & 0xff); error = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*protocol*/ AP_PROTO_DMA | AP_EXTEND, /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_SECT_CNT | AP_FLAG_TDIR_FROM_DEV, /*features*/ 0, /*sector_count*/ 2, /*lba*/ lba, /*command*/ ATA_READ_LOG_DMA_EXT, /*auxiliary*/ 0, /*data_ptr*/ log_buf, /*dxfer_len*/ sizeof(log_buf), /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 1, /*devtype*/ devtype); if (error != 0) { warnx("%s: build_ata_cmd() failed, likely programmer error", __func__); goto bailout; } if (retry_count > 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; error = cam_send_ccb(device, ccb); if (error != 0) { warn("error sending ATA READ LOG EXT CCB"); error = 1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); error = 1; goto bailout; } idle_log = (struct ata_power_cond_log_idle *)log_buf; standby_log = (struct ata_power_cond_log_standby *)&log_buf[sizeof(*idle_log)]; printf("ATA Power Conditions Log:\n"); printf(" Idle power conditions page:\n"); printf(" Idle A condition:\n"); epc_print_pcl_desc(&idle_log->idle_a_desc, " "); printf(" Idle B condition:\n"); epc_print_pcl_desc(&idle_log->idle_b_desc, " "); printf(" Idle C condition:\n"); epc_print_pcl_desc(&idle_log->idle_c_desc, " "); printf(" Standby power conditions page:\n"); printf(" Standby Y condition:\n"); epc_print_pcl_desc(&standby_log->standby_y_desc, " "); printf(" Standby Z condition:\n"); epc_print_pcl_desc(&standby_log->standby_z_desc, " "); bailout: return (error); } static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout, int power_only) { struct ata_params *ident = NULL; struct ata_identify_log_sup_cap sup_cap; const char *mode_name = NULL; uint8_t error = 0, ata_device = 0, status = 0; uint16_t count = 0; uint64_t lba = 0; uint32_t page_number, log_address; uint64_t caps = 0; int avail_bytes = 0; int res_available = 0; int retval; retval = 0; if (power_only != 0) goto check_power_mode; /* * Get standard ATA Identify data. */ retval = ata_do_identify(device, retry_count, timeout, ccb, &ident); if (retval != 0) { warnx("Couldn't get identify data"); goto bailout; } /* * Get the ATA Identify Data Log (0x30), * Supported Capabilities Page (0x03). */ log_address = ATA_IDENTIFY_DATA_LOG; page_number = ATA_IDL_SUP_CAP; lba = (((uint64_t)page_number & 0xff00) << 32) | ((page_number & 0x00ff) << 8) | (log_address & 0xff); bzero(&sup_cap, sizeof(sup_cap)); /* * XXX KDM check the supported protocol. */ retval = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*protocol*/ AP_PROTO_DMA | AP_EXTEND, /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_SECT_CNT | AP_FLAG_TDIR_FROM_DEV, /*features*/ 0, /*sector_count*/ 1, /*lba*/ lba, /*command*/ ATA_READ_LOG_DMA_EXT, /*auxiliary*/ 0, /*data_ptr*/ (uint8_t *)&sup_cap, /*dxfer_len*/ sizeof(sup_cap), /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 1, /*devtype*/ devtype); if (retval != 0) { warnx("%s: build_ata_cmd() failed, likely a programmer error", __func__); goto bailout; } if (retry_count > 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; retval = cam_send_ccb(device, ccb); if (retval != 0) { warn("error sending ATA READ LOG CCB"); retval = 1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); retval = 1; goto bailout; } if (ccb->ccb_h.func_code == XPT_SCSI_IO) { avail_bytes = ccb->csio.dxfer_len - ccb->csio.resid; } else { avail_bytes = ccb->ataio.dxfer_len - ccb->ataio.resid; } if (avail_bytes < (int)sizeof(sup_cap)) { warnx("Couldn't get enough of the ATA Supported " "Capabilities log, %d bytes returned", avail_bytes); retval = 1; goto bailout; } caps = le64dec(sup_cap.sup_cap); if ((caps & ATA_SUP_CAP_VALID) == 0) { warnx("Supported capabilities bits are not valid"); retval = 1; goto bailout; } printf("APM: %sSupported, %sEnabled\n", (ident->support.command2 & ATA_SUPPORT_APM) ? "" : "NOT ", (ident->enabled.command2 & ATA_SUPPORT_APM) ? "" : "NOT "); printf("EPC: %sSupported, %sEnabled\n", (ident->support2 & ATA_SUPPORT_EPC) ? "" : "NOT ", (ident->enabled2 & ATA_ENABLED_EPC) ? "" : "NOT "); printf("Low Power Standby %sSupported\n", (caps & ATA_SC_LP_STANDBY_SUP) ? "" : "NOT "); printf("Set EPC Power Source %sSupported\n", (caps & ATA_SC_SET_EPC_PS_SUP) ? "" : "NOT "); check_power_mode: retval = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*protocol*/ AP_PROTO_NON_DATA | AP_EXTEND, /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_NO_DATA | AP_FLAG_CHK_COND, /*features*/ ATA_SF_EPC, /*sector_count*/ 0, /*lba*/ 0, /*command*/ ATA_CHECK_POWER_MODE, /*auxiliary*/ 0, /*data_ptr*/ NULL, /*dxfer_len*/ 0, /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 0, /*devtype*/ devtype); if (retval != 0) { warnx("%s: build_ata_cmd() failed, likely a programmer error", __func__); goto bailout; } if (retry_count > 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; retval = cam_send_ccb(device, ccb); if (retval != 0) { warn("error sending ATA CHECK POWER MODE CCB"); retval = 1; goto bailout; } /* * Check to see whether we got the requested ATA result if this * is an SCSI ATA PASS-THROUGH command. */ if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)) { int error_code, sense_key, asc, ascq; retval = scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq); if (retval == 0) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); retval = 1; goto bailout; } if ((sense_key == SSD_KEY_RECOVERED_ERROR) && (asc == 0x00) && (ascq == 0x1d)) { res_available = 1; } } if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && (res_available == 0)) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); retval = 1; goto bailout; } retval = get_ata_status(device, ccb, &error, &count, &lba, &ata_device, &status); if (retval != 0) { warnx("Unable to get ATA CHECK POWER MODE result"); retval = 1; goto bailout; } mode_name = scsi_nv_to_str(epc_power_cond_map, - sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0]), count); + nitems(epc_power_cond_map), count); printf("Current power state: "); /* Note: ident can be null in power_only mode */ if ((ident == NULL) || (ident->enabled2 & ATA_ENABLED_EPC)) { if (mode_name != NULL) printf("%s", mode_name); else if (count == ATA_PM_ACTIVE_IDLE) { printf("PM0:Active or PM1:Idle"); } } else { switch (count) { case ATA_PM_STANDBY: printf("PM2:Standby"); break; case ATA_PM_IDLE: printf("PM1:Idle"); break; case ATA_PM_ACTIVE_IDLE: printf("PM0:Active or PM1:Idle"); break; } } printf("(0x%02x)\n", count); if (power_only != 0) goto bailout; if (caps & ATA_SC_LP_STANDBY_SUP) { uint32_t wait_mode; wait_mode = (lba >> 20) & 0xff; if (wait_mode == 0xff) { printf("Device not waiting to enter lower power " "condition"); } else { mode_name = scsi_nv_to_str(epc_power_cond_map, sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0]), wait_mode); printf("Device waiting to enter mode %s (0x%02x)\n", (mode_name != NULL) ? mode_name : "Unknown", wait_mode); } printf("Device is %sheld in the current power condition\n", (lba & 0x80000) ? "" : "NOT "); } bailout: return (retval); } static int epc_set_features(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, int retry_count, int timeout, int action, int power_cond, int timer, int enable, int save, int delayed_entry, int hold, int power_src, int restore_src) { uint64_t lba; uint16_t count = 0; int error; error = 0; lba = action; switch (action) { case ATA_SF_EPC_SET_TIMER: lba |= ((timer << ATA_SF_EPC_TIMER_SHIFT) & ATA_SF_EPC_TIMER_MASK); /* FALLTHROUGH */ case ATA_SF_EPC_SET_STATE: lba |= (enable ? ATA_SF_EPC_TIMER_EN : 0) | (save ? ATA_SF_EPC_TIMER_SAVE : 0); count = power_cond; break; case ATA_SF_EPC_GOTO: count = power_cond; lba |= (delayed_entry ? ATA_SF_EPC_GOTO_DELAY : 0) | (hold ? ATA_SF_EPC_GOTO_HOLD : 0); break; case ATA_SF_EPC_RESTORE: lba |= restore_src | (save ? ATA_SF_EPC_RST_SAVE : 0); break; case ATA_SF_EPC_ENABLE: case ATA_SF_EPC_DISABLE: break; case ATA_SF_EPC_SET_SOURCE: count = power_src; break; } error = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*protocol*/ AP_PROTO_NON_DATA | AP_EXTEND, /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_NO_DATA | AP_FLAG_TDIR_FROM_DEV, /*features*/ ATA_SF_EPC, /*sector_count*/ count, /*lba*/ lba, /*command*/ ATA_SETFEATURES, /*auxiliary*/ 0, /*data_ptr*/ NULL, /*dxfer_len*/ 0, /*cdb_storage*/ NULL, /*cdb_storage_len*/ 0, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 1, /*devtype*/ devtype); if (error != 0) { warnx("%s: build_ata_cmd() failed, likely a programmer error", __func__); goto bailout; } if (retry_count > 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; error = cam_send_ccb(device, ccb); if (error != 0) { warn("error sending ATA SET FEATURES CCB"); error = 1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); error = 1; goto bailout; } bailout: return (error); } int epc(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout, int verbosemode __unused) { union ccb *ccb = NULL; int error = 0; int c; int action = -1; camcontrol_devtype devtype; double timer_val = -1; int timer_tenths = 0, power_cond = -1; int delayed_entry = 0, hold = 0; int enable = -1, save = 0; int restore_src = -1; int power_src = -1; int power_only = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("%s: error allocating CCB", __func__); error = 1; goto bailout; } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'c': { scsi_nv_status status; int entry_num; status = scsi_get_nv(epc_cmd_map, - (sizeof(epc_cmd_map) / sizeof(epc_cmd_map[0])), + nitems(epc_cmd_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) action = epc_cmd_map[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "epc command", optarg); error = 1; goto bailout; } break; } case 'd': enable = 0; break; case 'D': delayed_entry = 1; break; case 'e': enable = 1; break; case 'H': hold = 1; break; case 'p': { scsi_nv_status status; int entry_num; status = scsi_get_nv(epc_power_cond_map, (sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0])), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) power_cond =epc_power_cond_map[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "power condition", optarg); error = 1; goto bailout; } break; } case 'P': power_only = 1; break; case 'r': { scsi_nv_status status; int entry_num; status = scsi_get_nv(epc_rst_val, (sizeof(epc_rst_val) / sizeof(epc_rst_val[0])), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) restore_src = epc_rst_val[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "restore value source", optarg); error = 1; goto bailout; } break; } case 's': save = 1; break; case 'S': { scsi_nv_status status; int entry_num; status = scsi_get_nv(epc_ps_map, - (sizeof(epc_ps_map) / sizeof(epc_ps_map[0])), + nitems(epc_ps_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) power_src = epc_ps_map[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "power source", optarg); error = 1; goto bailout; } break; } case 'T': { char *endptr; timer_val = strtod(optarg, &endptr); if (timer_val < 0) { warnx("Invalid timer value %f", timer_val); error = 1; goto bailout; } else if (*endptr != '\0') { warnx("Invalid timer value %s", optarg); error = 1; goto bailout; } timer_tenths = timer_val * 10; break; } default: break; } } if (action == -1) { warnx("Must specify an action"); error = 1; goto bailout; } error = get_device_type(device, retry_count, timeout, /*printerrors*/ 1, &devtype); if (error != 0) errx(1, "Unable to determine device type"); switch (devtype) { case CC_DT_ATA: case CC_DT_SATL: break; default: warnx("The epc subcommand only works with ATA protocol " "devices"); error = 1; goto bailout; break; /*NOTREACHED*/ } switch (action) { case ATA_SF_EPC_SET_TIMER: if (timer_val == -1) { warnx("Must specify a timer value (-T time)"); error = 1; } /* FALLTHROUGH */ case ATA_SF_EPC_SET_STATE: if (enable == -1) { warnx("Must specify enable (-e) or disable (-d)"); error = 1; } /* FALLTHROUGH */ case ATA_SF_EPC_GOTO: if (power_cond == -1) { warnx("Must specify a power condition with -p"); error = 1; } if (error != 0) goto bailout; break; case ATA_SF_EPC_SET_SOURCE: if (power_src == -1) { warnx("Must specify a power source (-S battery or " "-S notbattery) value"); error = 1; goto bailout; } break; case ATA_SF_EPC_RESTORE: if (restore_src == -1) { warnx("Must specify a source for restored value, " "-r default or -r saved"); error = 1; goto bailout; } break; case ATA_SF_EPC_ENABLE: case ATA_SF_EPC_DISABLE: case CCTL_EPC_GET_STATUS: case CCTL_EPC_LIST: default: break; } switch (action) { case CCTL_EPC_GET_STATUS: error = epc_getmode(device, devtype, ccb, retry_count, timeout, power_only); break; case CCTL_EPC_LIST: error = epc_list(device, devtype, ccb, retry_count, timeout); break; case ATA_SF_EPC_RESTORE: case ATA_SF_EPC_GOTO: case ATA_SF_EPC_SET_TIMER: case ATA_SF_EPC_SET_STATE: case ATA_SF_EPC_ENABLE: case ATA_SF_EPC_DISABLE: case ATA_SF_EPC_SET_SOURCE: error = epc_set_features(device, devtype, ccb, retry_count, timeout, action, power_cond, timer_tenths, enable, save, delayed_entry, hold, power_src, restore_src); break; default: warnx("Not implemented yet"); error = 1; goto bailout; break; } bailout: if (ccb != NULL) cam_freeccb(ccb); return (error); } diff --git a/sbin/camcontrol/zone.c b/sbin/camcontrol/zone.c index d651ca0e2acf..dc87cd9a9570 100644 --- a/sbin/camcontrol/zone.c +++ b/sbin/camcontrol/zone.c @@ -1,672 +1,671 @@ /*- * Copyright (c) 2015, 2016 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * Authors: Ken Merry (Spectra Logic Corporation) */ /* * SCSI and ATA Shingled Media Recording (SMR) support for camcontrol(8). * This is an implementation of the SCSI ZBC and ATA ZAC specs. */ -#include +#include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camcontrol.h" static struct scsi_nv zone_cmd_map[] = { { "rz", ZBC_IN_SA_REPORT_ZONES }, { "reportzones", ZBC_IN_SA_REPORT_ZONES }, { "close", ZBC_OUT_SA_CLOSE }, { "finish", ZBC_OUT_SA_FINISH }, { "open", ZBC_OUT_SA_OPEN }, { "rwp", ZBC_OUT_SA_RWP } }; static struct scsi_nv zone_rep_opts[] = { { "all", ZBC_IN_REP_ALL_ZONES }, { "empty", ZBC_IN_REP_EMPTY }, { "imp_open", ZBC_IN_REP_IMP_OPEN }, { "exp_open", ZBC_IN_REP_EXP_OPEN }, { "closed", ZBC_IN_REP_CLOSED }, { "full", ZBC_IN_REP_FULL }, { "readonly", ZBC_IN_REP_READONLY }, { "ro", ZBC_IN_REP_READONLY }, { "offline", ZBC_IN_REP_OFFLINE }, { "rwp", ZBC_IN_REP_RESET }, { "reset", ZBC_IN_REP_RESET }, { "nonseq", ZBC_IN_REP_NON_SEQ }, { "nonwp", ZBC_IN_REP_NON_WP } }; typedef enum { ZONE_OF_NORMAL = 0x00, ZONE_OF_SUMMARY = 0x01, ZONE_OF_SCRIPT = 0x02 } zone_output_flags; static struct scsi_nv zone_print_opts[] = { { "normal", ZONE_OF_NORMAL }, { "summary", ZONE_OF_SUMMARY }, { "script", ZONE_OF_SCRIPT } }; #define ZAC_ATA_SECTOR_COUNT(bcount) (((bcount) / 512) & 0xffff) typedef enum { ZONE_PRINT_OK, ZONE_PRINT_MORE_DATA, ZONE_PRINT_ERROR } zone_print_status; typedef enum { ZONE_FW_START, ZONE_FW_LEN, ZONE_FW_WP, ZONE_FW_TYPE, ZONE_FW_COND, ZONE_FW_SEQ, ZONE_FW_RESET, ZONE_NUM_FIELDS } zone_field_widths; zone_print_status zone_rz_print(uint8_t *data_ptr, uint32_t valid_len, int ata_format, zone_output_flags out_flags, int first_pass, uint64_t *next_start_lba); zone_print_status zone_rz_print(uint8_t *data_ptr, uint32_t valid_len, int ata_format, zone_output_flags out_flags, int first_pass, uint64_t *next_start_lba) { struct scsi_report_zones_hdr *hdr = NULL; struct scsi_report_zones_desc *desc = NULL; uint32_t hdr_len, len; uint64_t max_lba, next_lba = 0; zone_print_status status = ZONE_PRINT_OK; char tmpstr[80]; int field_widths[ZONE_NUM_FIELDS]; char word_sep; if (valid_len < sizeof(*hdr)) { status = ZONE_PRINT_ERROR; goto bailout; } hdr = (struct scsi_report_zones_hdr *)data_ptr; field_widths[ZONE_FW_START] = 11; field_widths[ZONE_FW_LEN] = 6; field_widths[ZONE_FW_WP] = 11; field_widths[ZONE_FW_TYPE] = 13; field_widths[ZONE_FW_COND] = 13; field_widths[ZONE_FW_SEQ] = 14; field_widths[ZONE_FW_RESET] = 16; if (ata_format == 0) { hdr_len = scsi_4btoul(hdr->length); max_lba = scsi_8btou64(hdr->maximum_lba); } else { hdr_len = le32dec(hdr->length); max_lba = le64dec(hdr->maximum_lba); } if (hdr_len > (valid_len + sizeof(*hdr))) { status = ZONE_PRINT_MORE_DATA; } len = MIN(valid_len - sizeof(*hdr), hdr_len); if (out_flags == ZONE_OF_SCRIPT) word_sep = '_'; else word_sep = ' '; if ((out_flags != ZONE_OF_SCRIPT) && (first_pass != 0)) { printf("%zu zones, Maximum LBA %#jx (%ju)\n", hdr_len / sizeof(*desc), (uintmax_t)max_lba, (uintmax_t)max_lba); switch (hdr->byte4 & SRZ_SAME_MASK) { case SRZ_SAME_ALL_DIFFERENT: printf("Zone lengths and types may vary\n"); break; case SRZ_SAME_ALL_SAME: printf("Zone lengths and types are all the same\n"); break; case SRZ_SAME_LAST_DIFFERENT: printf("Zone types are the same, last zone length " "differs\n"); break; case SRZ_SAME_TYPES_DIFFERENT: printf("Zone lengths are the same, types vary\n"); break; default: printf("Unknown SAME field value %#x\n", hdr->byte4 & SRZ_SAME_MASK); break; } } if (out_flags == ZONE_OF_SUMMARY) { status = ZONE_PRINT_OK; goto bailout; } if ((out_flags == ZONE_OF_NORMAL) && (first_pass != 0)) { printf("%*s %*s %*s %*s %*s %*s %*s\n", field_widths[ZONE_FW_START], "Start LBA", field_widths[ZONE_FW_LEN], "Length", field_widths[ZONE_FW_WP], "WP LBA", field_widths[ZONE_FW_TYPE], "Zone Type", field_widths[ZONE_FW_COND], "Condition", field_widths[ZONE_FW_SEQ], "Sequential", field_widths[ZONE_FW_RESET], "Reset"); } for (desc = &hdr->desc_list[0]; len >= sizeof(*desc); len -= sizeof(*desc), desc++) { uint64_t length, start_lba, wp_lba; if (ata_format == 0) { length = scsi_8btou64(desc->zone_length); start_lba = scsi_8btou64(desc->zone_start_lba); wp_lba = scsi_8btou64(desc->write_pointer_lba); } else { length = le64dec(desc->zone_length); start_lba = le64dec(desc->zone_start_lba); wp_lba = le64dec(desc->write_pointer_lba); } printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START], (uintmax_t)start_lba, field_widths[ZONE_FW_LEN], (uintmax_t)length, field_widths[ZONE_FW_WP], (uintmax_t)wp_lba); switch (desc->zone_type & SRZ_TYPE_MASK) { case SRZ_TYPE_CONVENTIONAL: snprintf(tmpstr, sizeof(tmpstr), "Conventional"); break; case SRZ_TYPE_SEQ_PREFERRED: case SRZ_TYPE_SEQ_REQUIRED: snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s", word_sep, ((desc->zone_type & SRZ_TYPE_MASK) == SRZ_TYPE_SEQ_PREFERRED) ? "Preferred" : "Required"); break; default: snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x", word_sep, word_sep,desc->zone_type & SRZ_TYPE_MASK); break; } printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr); switch (desc->zone_flags & SRZ_ZONE_COND_MASK) { case SRZ_ZONE_COND_NWP: snprintf(tmpstr, sizeof(tmpstr), "NWP"); break; case SRZ_ZONE_COND_EMPTY: snprintf(tmpstr, sizeof(tmpstr), "Empty"); break; case SRZ_ZONE_COND_IMP_OPEN: snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen", word_sep); break; case SRZ_ZONE_COND_EXP_OPEN: snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen", word_sep); break; case SRZ_ZONE_COND_CLOSED: snprintf(tmpstr, sizeof(tmpstr), "Closed"); break; case SRZ_ZONE_COND_READONLY: snprintf(tmpstr, sizeof(tmpstr), "Readonly"); break; case SRZ_ZONE_COND_FULL: snprintf(tmpstr, sizeof(tmpstr), "Full"); break; case SRZ_ZONE_COND_OFFLINE: snprintf(tmpstr, sizeof(tmpstr), "Offline"); break; default: snprintf(tmpstr, sizeof(tmpstr), "%#x", desc->zone_flags & SRZ_ZONE_COND_MASK); break; } printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr); if (desc->zone_flags & SRZ_ZONE_NON_SEQ) snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential", word_sep); else snprintf(tmpstr, sizeof(tmpstr), "Sequential"); printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr); if (desc->zone_flags & SRZ_ZONE_RESET) snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded", word_sep); else snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded", word_sep, word_sep); printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr); next_lba = start_lba + length; } bailout: *next_start_lba = next_lba; return (status); } int zone(struct cam_device *device, int argc, char **argv, char *combinedopt, int task_attr, int retry_count, int timeout, int verbosemode __unused) { union ccb *ccb = NULL; int action = -1, rep_option = -1; int all_zones = 0; uint64_t lba = 0; int error = 0; uint8_t *data_ptr = NULL; uint32_t alloc_len = 65536, valid_len = 0; camcontrol_devtype devtype; int ata_format = 0, use_ncq = 0; int first_pass = 1; zone_print_status zp_status; zone_output_flags out_flags = ZONE_OF_NORMAL; uint8_t *cdb_storage = NULL; int cdb_storage_len = 32; int c; ccb = cam_getccb(device); if (ccb == NULL) { warnx("%s: error allocating CCB", __func__); error = 1; goto bailout; } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': all_zones = 1; break; case 'c': { scsi_nv_status status; int entry_num; status = scsi_get_nv(zone_cmd_map, - (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])), + nitems(zone_cmd_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) action = zone_cmd_map[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "zone command", optarg); error = 1; goto bailout; } break; } case 'l': { char *endptr; lba = strtoull(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid lba argument %s", __func__, optarg); error = 1; goto bailout; } break; } case 'N': use_ncq = 1; break; case 'o': { scsi_nv_status status; int entry_num; status = scsi_get_nv(zone_rep_opts, - (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])), + nitems(zone_rep_opts), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) rep_option = zone_rep_opts[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "report zones", optarg); error = 1; goto bailout; } break; } case 'P': { scsi_nv_status status; int entry_num; status = scsi_get_nv(zone_print_opts, (sizeof(zone_print_opts) / sizeof(zone_print_opts[0])), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) out_flags = zone_print_opts[entry_num].value; else { warnx("%s: %s: %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "print", optarg); error = 1; goto bailout; } break; } default: break; } } if (action == -1) { warnx("%s: must specify -c ", __func__); error = 1; goto bailout; } error = get_device_type(device, retry_count, timeout, /*printerrors*/ 1, &devtype); if (error != 0) errx(1, "Unable to determine device type"); if (action == ZBC_IN_SA_REPORT_ZONES) { data_ptr = malloc(alloc_len); if (data_ptr == NULL) err(1, "unable to allocate %u bytes", alloc_len); restart_report: bzero(data_ptr, alloc_len); switch (devtype) { case CC_DT_SCSI: scsi_zbc_in(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*tag_action*/ task_attr, /*service_action*/ action, /*zone_start_lba*/ lba, /*zone_options*/ (rep_option != -1) ? rep_option : 0, /*data_ptr*/ data_ptr, /*dxfer_len*/ alloc_len, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000); break; case CC_DT_ATA: case CC_DT_SATL: { uint8_t command = 0; uint8_t protocol = 0; uint16_t features = 0, sector_count = 0; uint32_t auxiliary = 0; /* * XXX KDM support the partial bit? */ if (use_ncq == 0) { command = ATA_ZAC_MANAGEMENT_IN; features = action; if (rep_option != -1) features |= (rep_option << 8); sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len); protocol = AP_PROTO_DMA; } else { if (cdb_storage == NULL) cdb_storage = calloc(cdb_storage_len, 1); if (cdb_storage == NULL) err(1, "couldn't allocate memory"); command = ATA_RECV_FPDMA_QUEUED; features = ZAC_ATA_SECTOR_COUNT(alloc_len); sector_count = ATA_RFPDMA_ZAC_MGMT_IN << 8; auxiliary = action & 0xf; if (rep_option != -1) auxiliary |= rep_option << 8; protocol = AP_PROTO_FPDMA; } error = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS, /*tag_action*/ task_attr, /*protocol*/ protocol, /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_SECT_CNT | AP_FLAG_TDIR_FROM_DEV, /*features*/ features, /*sector_count*/ sector_count, /*lba*/ lba, /*command*/ command, /*auxiliary*/ auxiliary, /*data_ptr*/ data_ptr, /*dxfer_len*/ ZAC_ATA_SECTOR_COUNT(alloc_len)*512, /*cdb_storage*/ cdb_storage, /*cdb_storage_len*/ cdb_storage_len, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 1, /*devtype*/ devtype); if (error != 0) { warnx("%s: build_ata_cmd() failed, likely " "programmer error", __func__); goto bailout; } ata_format = 1; break; } default: warnx("%s: Unknown device type %d", __func__,devtype); error = 1; goto bailout; break; /*NOTREACHED*/ } } else { /* * XXX KDM the current methodology is to always send ATA * commands to ATA devices. Need to figure out how to * detect whether a SCSI to ATA translation layer will * translate ZBC IN/OUT commands to the appropriate ZAC * command. */ switch (devtype) { case CC_DT_SCSI: scsi_zbc_out(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*tag_action*/ task_attr, /*service_action*/ action, /*zone_id*/ lba, /*zone_flags*/ (all_zones != 0) ? ZBC_OUT_ALL : 0, /*data_ptr*/ NULL, /*dxfer_len*/ 0, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000); break; case CC_DT_ATA: case CC_DT_SATL: { uint8_t command = 0; uint8_t protocol = 0; uint16_t features = 0, sector_count = 0; uint32_t auxiliary = 0; /* * Note that we're taking advantage of the fact * that the action numbers are the same between the * ZBC and ZAC specs. */ if (use_ncq == 0) { protocol = AP_PROTO_NON_DATA; command = ATA_ZAC_MANAGEMENT_OUT; features = action & 0xf; if (all_zones != 0) features |= (ZBC_OUT_ALL << 8); } else { cdb_storage = calloc(cdb_storage_len, 1); if (cdb_storage == NULL) err(1, "couldn't allocate memory"); protocol = AP_PROTO_FPDMA; command = ATA_NCQ_NON_DATA; features = ATA_NCQ_ZAC_MGMT_OUT; auxiliary = action & 0xf; if (all_zones != 0) auxiliary |= (ZBC_OUT_ALL << 8); } error = build_ata_cmd(ccb, /*retry_count*/ retry_count, /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS, /*tag_action*/ task_attr, /*protocol*/ protocol, /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES | AP_FLAG_TLEN_NO_DATA, /*features*/ features, /*sector_count*/ sector_count, /*lba*/ lba, /*command*/ command, /*auxiliary*/ auxiliary, /*data_ptr*/ NULL, /*dxfer_len*/ 0, /*cdb_storage*/ cdb_storage, /*cdb_storage_len*/ cdb_storage_len, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000, /*is48bit*/ 1, /*devtype*/ devtype); if (error != 0) { warnx("%s: build_ata_cmd() failed, likely " "programmer error", __func__); goto bailout; } ata_format = 1; break; } default: warnx("%s: Unknown device type %d", __func__,devtype); error = 1; goto bailout; break; /*NOTREACHED*/ } } ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (retry_count > 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; error = cam_send_ccb(device, ccb); if (error != 0) { warn("error sending %s %s CCB", (devtype == CC_DT_SCSI) ? "ZBC" : "ZAC Management", (action == ZBC_IN_SA_REPORT_ZONES) ? "In" : "Out"); error = -1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); error = 1; goto bailout; } /* * If we aren't reading the list of zones, we're done. */ if (action != ZBC_IN_SA_REPORT_ZONES) goto bailout; if (ccb->ccb_h.func_code == XPT_SCSI_IO) valid_len = ccb->csio.dxfer_len - ccb->csio.resid; else valid_len = ccb->ataio.dxfer_len - ccb->ataio.resid; zp_status = zone_rz_print(data_ptr, valid_len, ata_format, out_flags, first_pass, &lba); if (zp_status == ZONE_PRINT_MORE_DATA) { bzero(ccb, sizeof(*ccb)); first_pass = 0; if (cdb_storage != NULL) bzero(cdb_storage, cdb_storage_len); goto restart_report; } else if (zp_status == ZONE_PRINT_ERROR) error = 1; bailout: if (ccb != NULL) cam_freeccb(ccb); free(data_ptr); free(cdb_storage); return (error); }