/* * Decode the SMP REPORT MANUFACTURER INFORMATION response. The format is * current as of SPL Revision 7, but the parsing should be backward * compatible for older versions of the spec. */ void smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response, int response_len, struct sbuf *sb) { char vendor[16], product[48], revision[16]; char comp_vendor[16]; sbuf_printf(sb, "Report Manufacturer Information\n"); sbuf_printf(sb, "Expander Change count: %d\n", scsi_2btoul(response->expander_change_count)); sbuf_printf(sb, "SAS 1.1 Format: %s\n", smp_yesno(response->sas_11_format & SMP_RMI_SAS11_FORMAT)); cam_strvis(vendor, response->vendor, sizeof(response->vendor), sizeof(vendor)); cam_strvis(product, response->product, sizeof(response->product), sizeof(product)); cam_strvis(revision, response->revision, sizeof(response->revision), sizeof(revision)); sbuf_printf(sb, "<%s %s %s>\n", vendor, product, revision); if ((response->sas_11_format & SMP_RMI_SAS11_FORMAT) == 0) { uint8_t *curbyte; int line_start, line_cursor; sbuf_printf(sb, "Vendor Specific Data:\n"); /* * Print out the bytes roughly in the style of hd(1), but * without the extra ASCII decoding. Hexadecimal line * numbers on the left, and 16 bytes per line, with an * extra space after the first 8 bytes. * * It would be nice if this sort of thing were available * in a library routine. */ for (curbyte = (uint8_t *)&response->comp_vendor, line_start= 1, line_cursor = 0; curbyte < (uint8_t *)&response->crc; curbyte++, line_cursor++) { if (line_start != 0) { sbuf_printf(sb, "%08lx ", (unsigned long)(curbyte - (uint8_t *)response)); line_start = 0; line_cursor = 0; } sbuf_printf(sb, "%02x", *curbyte); if (line_cursor == 15) { sbuf_printf(sb, "\n"); line_start = 1; } else sbuf_printf(sb, " %s", (line_cursor == 7) ? " " : ""); } if (line_cursor != 16) sbuf_printf(sb, "\n"); return; } cam_strvis(comp_vendor, response->comp_vendor, sizeof(response->comp_vendor), sizeof(comp_vendor)); sbuf_printf(sb, "Component Vendor: %s\n", comp_vendor); sbuf_printf(sb, "Component ID: %#x\n", scsi_2btoul(response->comp_id)); sbuf_printf(sb, "Component Revision: %#x\n", response->comp_revision); sbuf_printf(sb, "Vendor Specific: 0x%016jx\n", (uintmax_t)scsi_8btou64(response->vendor_specific)); }
/* * 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) {
/* * Decode the SMP REPORT GENERAL response. The format is current as of SPL * Revision 7, but the parsing should be backward compatible for older * versions of the spec. */ void smp_report_general_sbuf(struct smp_report_general_response *response, int response_len, struct sbuf *sb) { sbuf_printf(sb, "Report General\n"); sbuf_printf(sb, "Response Length: %d words (%d bytes)\n", response->response_len, response->response_len * SMP_WORD_LEN); sbuf_printf(sb, "Expander Change Count: %d\n", scsi_2btoul(response->expander_change_count)); sbuf_printf(sb, "Expander Route Indexes: %d\n", scsi_2btoul(response->expander_route_indexes)); sbuf_printf(sb, "Long Response: %s\n", smp_yesno(response->long_response & SMP_RG_LONG_RESPONSE)); sbuf_printf(sb, "Number of Phys: %d\n", response->num_phys); sbuf_printf(sb, "Table to Table Supported: %s\n", smp_yesno(response->config_bits0 & SMP_RG_TABLE_TO_TABLE_SUP)); sbuf_printf(sb, "Zone Configuring: %s\n", smp_yesno(response->config_bits0 & SMP_RG_ZONE_CONFIGURING)); sbuf_printf(sb, "Self Configuring: %s\n", smp_yesno(response->config_bits0 & SMP_RG_SELF_CONFIGURING)); sbuf_printf(sb, "STP Continue AWT: %s\n", smp_yesno(response->config_bits0 & SMP_RG_STP_CONTINUE_AWT)); sbuf_printf(sb, "Open Reject Retry Supported: %s\n", smp_yesno(response->config_bits0 & SMP_RG_OPEN_REJECT_RETRY_SUP)); sbuf_printf(sb, "Configures Others: %s\n", smp_yesno(response->config_bits0 & SMP_RG_CONFIGURES_OTHERS)); sbuf_printf(sb, "Configuring: %s\n", smp_yesno(response->config_bits0 & SMP_RG_CONFIGURING)); sbuf_printf(sb, "Externally Configurable Route Table: %s\n", smp_yesno(response->config_bits0 & SMP_RG_CONFIGURING)); sbuf_printf(sb, "Enclosure Logical Identifier: 0x%016jx\n", (uintmax_t)scsi_8btou64(response->encl_logical_id)); /* * If the response->response_len is 0, then we don't have the * extended information. Also, if the user didn't allocate enough * space for the full request, don't try to parse it. */ if ((response->response_len == 0) || (response_len < (sizeof(struct smp_report_general_response) - sizeof(response->crc)))) return; sbuf_printf(sb, "STP Bus Inactivity Time Limit: %d\n", scsi_2btoul(response->stp_bus_inact_time_limit)); sbuf_printf(sb, "STP Maximum Connect Time Limit: %d\n", scsi_2btoul(response->stp_max_conn_time_limit)); sbuf_printf(sb, "STP SMP I_T Nexus Loss Time: %d\n", scsi_2btoul(response->stp_smp_it_nexus_loss_time)); sbuf_printf(sb, "Number of Zone Groups: %d\n", (response->config_bits1 & SMP_RG_NUM_ZONE_GROUPS_MASK) >> SMP_RG_NUM_ZONE_GROUPS_SHIFT); sbuf_printf(sb, "Zone Locked: %s\n", smp_yesno(response->config_bits1 & SMP_RG_ZONE_LOCKED)); sbuf_printf(sb, "Physical Presence Supported: %s\n", smp_yesno(response->config_bits1 & SMP_RG_PP_SUPPORTED)); sbuf_printf(sb, "Physical Presence Asserted: %s\n", smp_yesno(response->config_bits1 & SMP_RG_PP_ASSERTED)); sbuf_printf(sb, "Zoning Supported: %s\n", smp_yesno(response->config_bits1 & SMP_RG_ZONING_SUPPORTED)); sbuf_printf(sb, "Zoning Enabled: %s\n", smp_yesno(response->config_bits1 & SMP_RG_ZONING_ENABLED)); sbuf_printf(sb, "Saving: %s\n", smp_yesno(response->config_bits2 & SMP_RG_SAVING)); sbuf_printf(sb, "Saving Zone Manager Password Supported: %s\n", smp_yesno(response->config_bits2 & SMP_RG_SAVING_ZM_PWD_SUP)); sbuf_printf(sb, "Saving Zone Phy Information Supported: %s\n", smp_yesno(response->config_bits2 & SMP_RG_SAVING_PHY_INFO_SUP)); sbuf_printf(sb, "Saving Zone Permission Table Supported: %s\n", smp_yesno(response->config_bits2 & SMP_RG_SAVING_ZPERM_TAB_SUP)); sbuf_printf(sb, "Saving Zoning Enabled Supported: %s\n", smp_yesno(response->config_bits2 & SMP_RG_SAVING_ZENABLED_SUP)); sbuf_printf(sb, "Maximum Number of Routed SAS Addresses: %d\n", scsi_2btoul(response->max_num_routed_addrs)); sbuf_printf(sb, "Active Zone Manager SAS Address: 0x%016jx\n", scsi_8btou64(response->active_zm_address)); sbuf_printf(sb, "Zone Inactivity Time Limit: %d\n", scsi_2btoul(response->zone_lock_inact_time_limit)); sbuf_printf(sb, "First Enclosure Connector Element Index: %d\n", response->first_encl_conn_el_index); sbuf_printf(sb, "Number of Enclosure Connector Element Indexes: %d\n", response->num_encl_conn_el_indexes); sbuf_printf(sb, "Reduced Functionality: %s\n", smp_yesno(response->reduced_functionality & SMP_RG_REDUCED_FUNCTIONALITY)); sbuf_printf(sb, "Time to Reduced Functionality: %d\n", response->time_to_reduced_func); sbuf_printf(sb, "Initial Time to Reduced Functionality: %d\n", response->initial_time_to_reduced_func); sbuf_printf(sb, "Maximum Reduced Functionality Time: %d\n", response->max_reduced_func_time); sbuf_printf(sb, "Last Self-Configuration Status Descriptor Index: %d\n", scsi_2btoul(response->last_sc_stat_desc_index)); sbuf_printf(sb, "Maximum Number of Storated Self-Configuration " "Status Descriptors: %d\n", scsi_2btoul(response->max_sc_stat_descs)); sbuf_printf(sb, "Last Phy Event List Descriptor Index: %d\n", scsi_2btoul(response->last_phy_evl_desc_index)); sbuf_printf(sb, "Maximum Number of Stored Phy Event List " "Descriptors: %d\n", scsi_2btoul(response->max_stored_pel_descs)); sbuf_printf(sb, "STP Reject to Open Limit: %d\n", scsi_2btoul(response->stp_reject_to_open_limit)); }