cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cypress_atacb.c (8181B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Support for emulating SAT (ata pass through) on devices based
      4 *       on the Cypress USB/ATA bridge supporting ATACB.
      5 *
      6 * Copyright (c) 2008 Matthieu Castet (castet.matthieu@free.fr)
      7 */
      8
      9#include <linux/module.h>
     10#include <scsi/scsi.h>
     11#include <scsi/scsi_cmnd.h>
     12#include <scsi/scsi_eh.h>
     13#include <linux/ata.h>
     14
     15#include "usb.h"
     16#include "protocol.h"
     17#include "scsiglue.h"
     18#include "debug.h"
     19
     20#define DRV_NAME "ums-cypress"
     21
     22MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB");
     23MODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>");
     24MODULE_LICENSE("GPL");
     25MODULE_IMPORT_NS(USB_STORAGE);
     26
     27/*
     28 * The table of devices
     29 */
     30#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
     31		    vendorName, productName, useProtocol, useTransport, \
     32		    initFunction, flags) \
     33{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
     34  .driver_info = (flags) }
     35
     36static struct usb_device_id cypress_usb_ids[] = {
     37#	include "unusual_cypress.h"
     38	{ }		/* Terminating entry */
     39};
     40MODULE_DEVICE_TABLE(usb, cypress_usb_ids);
     41
     42#undef UNUSUAL_DEV
     43
     44/*
     45 * The flags table
     46 */
     47#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
     48		    vendor_name, product_name, use_protocol, use_transport, \
     49		    init_function, Flags) \
     50{ \
     51	.vendorName = vendor_name,	\
     52	.productName = product_name,	\
     53	.useProtocol = use_protocol,	\
     54	.useTransport = use_transport,	\
     55	.initFunction = init_function,	\
     56}
     57
     58static struct us_unusual_dev cypress_unusual_dev_list[] = {
     59#	include "unusual_cypress.h"
     60	{ }		/* Terminating entry */
     61};
     62
     63#undef UNUSUAL_DEV
     64
     65
     66/*
     67 * ATACB is a protocol used on cypress usb<->ata bridge to
     68 * send raw ATA command over mass storage
     69 * There is a ATACB2 protocol that support LBA48 on newer chip.
     70 * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf
     71 * datasheet from cypress.com.
     72 */
     73static void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us)
     74{
     75	unsigned char save_cmnd[MAX_COMMAND_SIZE];
     76
     77	if (likely(srb->cmnd[0] != ATA_16 && srb->cmnd[0] != ATA_12)) {
     78		usb_stor_transparent_scsi_command(srb, us);
     79		return;
     80	}
     81
     82	memcpy(save_cmnd, srb->cmnd, sizeof(save_cmnd));
     83	memset(srb->cmnd, 0, MAX_COMMAND_SIZE);
     84
     85	/* check if we support the command */
     86	if (save_cmnd[1] >> 5) /* MULTIPLE_COUNT */
     87		goto invalid_fld;
     88	/* check protocol */
     89	switch ((save_cmnd[1] >> 1) & 0xf) {
     90	case 3: /*no DATA */
     91	case 4: /* PIO in */
     92	case 5: /* PIO out */
     93		break;
     94	default:
     95		goto invalid_fld;
     96	}
     97
     98	/* first build the ATACB command */
     99	srb->cmd_len = 16;
    100
    101	srb->cmnd[0] = 0x24; /*
    102			      * bVSCBSignature : vendor-specific command
    103			      * this value can change, but most(all ?) manufacturers
    104			      * keep the cypress default : 0x24
    105			      */
    106	srb->cmnd[1] = 0x24; /* bVSCBSubCommand : 0x24 for ATACB */
    107
    108	srb->cmnd[3] = 0xff - 1; /*
    109				  * features, sector count, lba low, lba med
    110				  * lba high, device, command are valid
    111				  */
    112	srb->cmnd[4] = 1; /* TransferBlockCount : 512 */
    113
    114	if (save_cmnd[0] == ATA_16) {
    115		srb->cmnd[ 6] = save_cmnd[ 4]; /* features */
    116		srb->cmnd[ 7] = save_cmnd[ 6]; /* sector count */
    117		srb->cmnd[ 8] = save_cmnd[ 8]; /* lba low */
    118		srb->cmnd[ 9] = save_cmnd[10]; /* lba med */
    119		srb->cmnd[10] = save_cmnd[12]; /* lba high */
    120		srb->cmnd[11] = save_cmnd[13]; /* device */
    121		srb->cmnd[12] = save_cmnd[14]; /* command */
    122
    123		if (save_cmnd[1] & 0x01) {/* extended bit set for LBA48 */
    124			/* this could be supported by atacb2 */
    125			if (save_cmnd[3] || save_cmnd[5] || save_cmnd[7] || save_cmnd[9]
    126					|| save_cmnd[11])
    127				goto invalid_fld;
    128		}
    129	} else { /* ATA12 */
    130		srb->cmnd[ 6] = save_cmnd[3]; /* features */
    131		srb->cmnd[ 7] = save_cmnd[4]; /* sector count */
    132		srb->cmnd[ 8] = save_cmnd[5]; /* lba low */
    133		srb->cmnd[ 9] = save_cmnd[6]; /* lba med */
    134		srb->cmnd[10] = save_cmnd[7]; /* lba high */
    135		srb->cmnd[11] = save_cmnd[8]; /* device */
    136		srb->cmnd[12] = save_cmnd[9]; /* command */
    137
    138	}
    139	/* Filter SET_FEATURES - XFER MODE command */
    140	if ((srb->cmnd[12] == ATA_CMD_SET_FEATURES)
    141			&& (srb->cmnd[6] == SETFEATURES_XFER))
    142		goto invalid_fld;
    143
    144	if (srb->cmnd[12] == ATA_CMD_ID_ATA || srb->cmnd[12] == ATA_CMD_ID_ATAPI)
    145		srb->cmnd[2] |= (1<<7); /* set  IdentifyPacketDevice for these cmds */
    146
    147
    148	usb_stor_transparent_scsi_command(srb, us);
    149
    150	/* if the device doesn't support ATACB */
    151	if (srb->result == SAM_STAT_CHECK_CONDITION &&
    152			memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB,
    153				sizeof(usb_stor_sense_invalidCDB)) == 0) {
    154		usb_stor_dbg(us, "cypress atacb not supported ???\n");
    155		goto end;
    156	}
    157
    158	/*
    159	 * if ck_cond flags is set, and there wasn't critical error,
    160	 * build the special sense
    161	 */
    162	if ((srb->result != (DID_ERROR << 16) &&
    163				srb->result != (DID_ABORT << 16)) &&
    164			save_cmnd[2] & 0x20) {
    165		struct scsi_eh_save ses;
    166		unsigned char regs[8];
    167		unsigned char *sb = srb->sense_buffer;
    168		unsigned char *desc = sb + 8;
    169		int tmp_result;
    170
    171		/* build the command for reading the ATA registers */
    172		scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sizeof(regs));
    173
    174		/*
    175		 * we use the same command as before, but we set
    176		 * the read taskfile bit, for not executing atacb command,
    177		 * but reading register selected in srb->cmnd[4]
    178		 */
    179		srb->cmd_len = 16;
    180		srb->cmnd[2] = 1;
    181
    182		usb_stor_transparent_scsi_command(srb, us);
    183		memcpy(regs, srb->sense_buffer, sizeof(regs));
    184		tmp_result = srb->result;
    185		scsi_eh_restore_cmnd(srb, &ses);
    186		/* we fail to get registers, report invalid command */
    187		if (tmp_result != SAM_STAT_GOOD)
    188			goto invalid_fld;
    189
    190		/* build the sense */
    191		memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
    192
    193		/* set sk, asc for a good command */
    194		sb[1] = RECOVERED_ERROR;
    195		sb[2] = 0; /* ATA PASS THROUGH INFORMATION AVAILABLE */
    196		sb[3] = 0x1D;
    197
    198		/*
    199		 * XXX we should generate sk, asc, ascq from status and error
    200		 * regs
    201		 * (see 11.1 Error translation ATA device error to SCSI error
    202		 * map, and ata_to_sense_error from libata.)
    203		 */
    204
    205		/* Sense data is current and format is descriptor. */
    206		sb[0] = 0x72;
    207		desc[0] = 0x09; /* ATA_RETURN_DESCRIPTOR */
    208
    209		/* set length of additional sense data */
    210		sb[7] = 14;
    211		desc[1] = 12;
    212
    213		/* Copy registers into sense buffer. */
    214		desc[ 2] = 0x00;
    215		desc[ 3] = regs[1];  /* features */
    216		desc[ 5] = regs[2];  /* sector count */
    217		desc[ 7] = regs[3];  /* lba low */
    218		desc[ 9] = regs[4];  /* lba med */
    219		desc[11] = regs[5];  /* lba high */
    220		desc[12] = regs[6];  /* device */
    221		desc[13] = regs[7];  /* command */
    222
    223		srb->result = SAM_STAT_CHECK_CONDITION;
    224	}
    225	goto end;
    226invalid_fld:
    227	srb->result = SAM_STAT_CHECK_CONDITION;
    228
    229	memcpy(srb->sense_buffer,
    230			usb_stor_sense_invalidCDB,
    231			sizeof(usb_stor_sense_invalidCDB));
    232end:
    233	memcpy(srb->cmnd, save_cmnd, sizeof(save_cmnd));
    234	if (srb->cmnd[0] == ATA_12)
    235		srb->cmd_len = 12;
    236}
    237
    238static struct scsi_host_template cypress_host_template;
    239
    240static int cypress_probe(struct usb_interface *intf,
    241			 const struct usb_device_id *id)
    242{
    243	struct us_data *us;
    244	int result;
    245	struct usb_device *device;
    246
    247	result = usb_stor_probe1(&us, intf, id,
    248			(id - cypress_usb_ids) + cypress_unusual_dev_list,
    249			&cypress_host_template);
    250	if (result)
    251		return result;
    252
    253	/*
    254	 * Among CY7C68300 chips, the A revision does not support Cypress ATACB
    255	 * Filter out this revision from EEPROM default descriptor values
    256	 */
    257	device = interface_to_usbdev(intf);
    258	if (device->descriptor.iManufacturer != 0x38 ||
    259	    device->descriptor.iProduct != 0x4e ||
    260	    device->descriptor.iSerialNumber != 0x64) {
    261		us->protocol_name = "Transparent SCSI with Cypress ATACB";
    262		us->proto_handler = cypress_atacb_passthrough;
    263	} else {
    264		us->protocol_name = "Transparent SCSI";
    265		us->proto_handler = usb_stor_transparent_scsi_command;
    266	}
    267
    268	result = usb_stor_probe2(us);
    269	return result;
    270}
    271
    272static struct usb_driver cypress_driver = {
    273	.name =		DRV_NAME,
    274	.probe =	cypress_probe,
    275	.disconnect =	usb_stor_disconnect,
    276	.suspend =	usb_stor_suspend,
    277	.resume =	usb_stor_resume,
    278	.reset_resume =	usb_stor_reset_resume,
    279	.pre_reset =	usb_stor_pre_reset,
    280	.post_reset =	usb_stor_post_reset,
    281	.id_table =	cypress_usb_ids,
    282	.soft_unbind =	1,
    283	.no_dynamic_id = 1,
    284};
    285
    286module_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME);