static rtems_status_code rtems_bsd_scsi_read_capacity(union ccb *ccb, uint32_t *block_count, uint32_t *block_size) { rtems_status_code sc = RTEMS_SUCCESSFUL; struct scsi_read_capacity_data rdcap; memset(&rdcap, 0, sizeof(rdcap)); scsi_read_capacity( &ccb->csio, BSD_SCSI_RETRIES, rtems_bsd_ccb_callback, BSD_SCSI_TAG, &rdcap, SSD_FULL_SIZE, BSD_SCSI_TIMEOUT ); sc = rtems_bsd_ccb_action(ccb); if (sc != RTEMS_SUCCESSFUL) { return RTEMS_IO_ERROR; } *block_size = scsi_4btoul(rdcap.length); *block_count = scsi_4btoul(rdcap.addr) + 1; return RTEMS_SUCCESSFUL; }
/* * Convert the SCSI command in ccb to an ata_xfer command in xa * for ATA_PORT_T_DISK operations. Set the completion function * to convert the response back, then dispatch to the OpenBSD AHCI * layer. * * AHCI DISK commands only support a limited command set, and we * fake additional commands to make it play nice with the CAM subsystem. */ static void ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx, union ccb *ccb) { struct ccb_hdr *ccbh; struct ccb_scsiio *csio; struct ata_xfer *xa; struct ata_port *at; struct ata_fis_h2d *fis; struct ata_pass_12 *atp12; struct ata_pass_16 *atp16; scsi_cdb_t cdb; union scsi_data *rdata; int rdata_len; u_int64_t capacity; u_int64_t lba; u_int32_t count; ccbh = &ccb->csio.ccb_h; csio = &ccb->csio; at = atx ? atx : ap->ap_ata[0]; /* * XXX not passing NULL at for direct attach! */ xa = ahci_ata_get_xfer(ap, atx); rdata = (void *)csio->data_ptr; rdata_len = csio->dxfer_len; /* * Build the FIS or process the csio to completion. */ cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ? csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); switch(cdb->generic.opcode) { case REQUEST_SENSE: /* * Auto-sense everything, so explicit sense requests * return no-sense. */ ccbh->status = CAM_SCSI_STATUS_ERROR; break; case INQUIRY: /* * Inquiry supported features * * [opcode, byte2, page_code, length, control] */ if (cdb->inquiry.byte2 & SI_EVPD) { ahci_xpt_page_inquiry(ap, at, ccb); } else { bzero(rdata, rdata_len); if (rdata_len < SHORT_INQUIRY_LENGTH) { ccbh->status = CAM_CCB_LEN_ERR; break; } if (rdata_len > sizeof(rdata->inquiry_data)) rdata_len = sizeof(rdata->inquiry_data); rdata->inquiry_data.device = T_DIRECT; rdata->inquiry_data.version = SCSI_REV_SPC2; rdata->inquiry_data.response_format = 2; rdata->inquiry_data.additional_length = 32; bcopy("SATA ", rdata->inquiry_data.vendor, 8); bcopy(at->at_identify.model, rdata->inquiry_data.product, sizeof(rdata->inquiry_data.product)); bcopy(at->at_identify.firmware, rdata->inquiry_data.revision, sizeof(rdata->inquiry_data.revision)); ccbh->status = CAM_REQ_CMP; } /* * Use the vendor specific area to set the TRIM status * for scsi_da */ if (at->at_identify.support_dsm) { rdata->inquiry_data.vendor_specific1[0] = at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM; rdata->inquiry_data.vendor_specific1[1] = at->at_identify.max_dsm_blocks; } break; case READ_CAPACITY_16: if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) { ccbh->status = CAM_REQ_INVALID; break; } if (rdata_len < sizeof(rdata->read_capacity_data_16)) { ccbh->status = CAM_CCB_LEN_ERR; break; } /* fall through */ case READ_CAPACITY: if (rdata_len < sizeof(rdata->read_capacity_data)) { ccbh->status = CAM_CCB_LEN_ERR; break; } capacity = at->at_capacity; bzero(rdata, rdata_len); if (cdb->generic.opcode == READ_CAPACITY) { rdata_len = sizeof(rdata->read_capacity_data); if (capacity > 0xFFFFFFFFU) capacity = 0xFFFFFFFFU; bzero(&rdata->read_capacity_data, rdata_len); scsi_ulto4b((u_int32_t)capacity - 1, rdata->read_capacity_data.addr); scsi_ulto4b(512, rdata->read_capacity_data.length); } else { rdata_len = sizeof(rdata->read_capacity_data_16); bzero(&rdata->read_capacity_data_16, rdata_len); scsi_u64to8b(capacity - 1, rdata->read_capacity_data_16.addr); scsi_ulto4b(512, rdata->read_capacity_data_16.length); } ccbh->status = CAM_REQ_CMP; break; case SYNCHRONIZE_CACHE: /* * Synchronize cache. Specification says this can take * greater then 30 seconds so give it at least 45. */ fis = xa->fis; fis->flags = ATA_H2D_FLAGS_CMD; fis->command = ATA_C_FLUSH_CACHE; fis->device = 0; if (xa->timeout < 45000) xa->timeout = 45000; xa->datalen = 0; xa->flags = 0; xa->complete = ahci_ata_complete_disk_synchronize_cache; break; case TRIM: fis = xa->fis; fis->command = ATA_C_DATA_SET_MANAGEMENT; fis->features = (u_int8_t)ATA_SF_DSM_TRIM; fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8); xa->flags = ATA_F_WRITE; fis->flags = ATA_H2D_FLAGS_CMD; xa->data = csio->data_ptr; xa->datalen = csio->dxfer_len; xa->timeout = ccbh->timeout*50; /* milliseconds */ fis->sector_count =(u_int8_t)(xa->datalen/512); fis->sector_count_exp =(u_int8_t)((xa->datalen/512)>>8); lba = 0; fis->lba_low = (u_int8_t)lba; fis->lba_mid = (u_int8_t)(lba >> 8); fis->lba_high = (u_int8_t)(lba >> 16); fis->lba_low_exp = (u_int8_t)(lba >> 24); fis->lba_mid_exp = (u_int8_t)(lba >> 32); fis->lba_high_exp = (u_int8_t)(lba >> 40); fis->device = ATA_H2D_DEVICE_LBA; xa->data = csio->data_ptr; xa->complete = ahci_ata_complete_disk_rw; ccbh->status = CAM_REQ_INPROG; break; case TEST_UNIT_READY: case START_STOP_UNIT: case PREVENT_ALLOW: /* * Just silently return success */ ccbh->status = CAM_REQ_CMP; rdata_len = 0; break; case ATA_PASS_12: atp12 = &cdb->ata_pass_12; fis = xa->fis; /* * Figure out the flags to be used, depending on the direction of the * CAM request. */ switch (ccbh->flags & CAM_DIR_MASK) { case CAM_DIR_IN: xa->flags = ATA_F_READ; break; case CAM_DIR_OUT: xa->flags = ATA_F_WRITE; break; default: xa->flags = 0; } xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE; xa->data = csio->data_ptr; xa->datalen = csio->dxfer_len; xa->complete = ahci_ata_complete_disk_rw; xa->timeout = ccbh->timeout; /* * Populate the fis from the information we received through CAM * ATA passthrough. */ fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp12->flags ? */ fis->features = atp12->features; fis->sector_count = atp12->sector_count; fis->lba_low = atp12->lba_low; fis->lba_mid = atp12->lba_mid; fis->lba_high = atp12->lba_high; fis->device = atp12->device; /* maybe always 0? */ fis->command = atp12->command; fis->control = atp12->control; /* * Mark as in progress so it is sent to the device. */ ccbh->status = CAM_REQ_INPROG; break; case ATA_PASS_16: atp16 = &cdb->ata_pass_16; fis = xa->fis; /* * Figure out the flags to be used, depending on the direction of the * CAM request. */ switch (ccbh->flags & CAM_DIR_MASK) { case CAM_DIR_IN: xa->flags = ATA_F_READ; break; case CAM_DIR_OUT: xa->flags = ATA_F_WRITE; break; default: xa->flags = 0; } xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE; xa->data = csio->data_ptr; xa->datalen = csio->dxfer_len; xa->complete = ahci_ata_complete_disk_rw; xa->timeout = ccbh->timeout; /* * Populate the fis from the information we received through CAM * ATA passthrough. */ fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp16->flags ? */ fis->features = atp16->features; fis->features_exp = atp16->features_ext; fis->sector_count = atp16->sector_count; fis->sector_count_exp = atp16->sector_count_ext; fis->lba_low = atp16->lba_low; fis->lba_low_exp = atp16->lba_low_ext; fis->lba_mid = atp16->lba_mid; fis->lba_mid_exp = atp16->lba_mid_ext; fis->lba_high = atp16->lba_high; fis->lba_mid_exp = atp16->lba_mid_ext; fis->device = atp16->device; /* maybe always 0? */ fis->command = atp16->command; /* * Mark as in progress so it is sent to the device. */ ccbh->status = CAM_REQ_INPROG; break; default: switch(cdb->generic.opcode) { case READ_6: lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; xa->flags = ATA_F_READ; break; case READ_10: lba = scsi_4btoul(cdb->rw_10.addr); count = scsi_2btoul(cdb->rw_10.length); xa->flags = ATA_F_READ; break; case READ_12: lba = scsi_4btoul(cdb->rw_12.addr); count = scsi_4btoul(cdb->rw_12.length); xa->flags = ATA_F_READ; break; case READ_16: lba = scsi_8btou64(cdb->rw_16.addr); count = scsi_4btoul(cdb->rw_16.length); xa->flags = ATA_F_READ; break; case WRITE_6: lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; xa->flags = ATA_F_WRITE; break; case WRITE_10: lba = scsi_4btoul(cdb->rw_10.addr); count = scsi_2btoul(cdb->rw_10.length); xa->flags = ATA_F_WRITE; break; case WRITE_12: lba = scsi_4btoul(cdb->rw_12.addr); count = scsi_4btoul(cdb->rw_12.length); xa->flags = ATA_F_WRITE; break; case WRITE_16: lba = scsi_8btou64(cdb->rw_16.addr); count = scsi_4btoul(cdb->rw_16.length); xa->flags = ATA_F_WRITE; break; default: ccbh->status = CAM_REQ_INVALID; break; } if (ccbh->status != CAM_REQ_INPROG) break; fis = xa->fis; fis->flags = ATA_H2D_FLAGS_CMD; fis->lba_low = (u_int8_t)lba; fis->lba_mid = (u_int8_t)(lba >> 8); fis->lba_high = (u_int8_t)(lba >> 16); fis->device = ATA_H2D_DEVICE_LBA; /* * NCQ only for direct-attached disks, do not currently * try to use NCQ with port multipliers. */ if (at->at_ncqdepth > 1 && ap->ap_type == ATA_PORT_T_DISK && (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) && (ccbh->flags & CAM_POLLED) == 0) { /* * Use NCQ - always uses 48 bit addressing */ xa->flags |= ATA_F_NCQ; fis->command = (xa->flags & ATA_F_WRITE) ? ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA; fis->lba_low_exp = (u_int8_t)(lba >> 24); fis->lba_mid_exp = (u_int8_t)(lba >> 32); fis->lba_high_exp = (u_int8_t)(lba >> 40); fis->sector_count = xa->tag << 3; fis->features = (u_int8_t)count; fis->features_exp = (u_int8_t)(count >> 8); } else if (count > 0x100 || lba > 0x0FFFFFFFU) {
int scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt, 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; } bzero(&(&ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); 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]), 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*/ MSG_SIMPLE_Q_TAG, /*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"); if (verbosemode != 0) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); } 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); }