Esempio n. 1
0
/*
 * 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));
}
Esempio n. 2
0
/*
 * 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) {
Esempio n. 3
0
/*
 * 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));
}