Commit 6ff265fc authored by Bean Huo's avatar Bean Huo Committed by Martin K. Petersen
Browse files

scsi: ufs: core: bsg: Add advanced RPMB support in ufs_bsg



Add advanced RPMB support in ufs_bsg:

 1. According to the UFS specification, only one RPMB operation can be
    performed at any time. We can ensure this by using reserved slot and
    its dev_cmd sync operation protection mechanism.

 2. For Advanced RPMB, RPMB metadata is packaged in an EHS (Extra Header
    Segment) of a command UPIU, and the corresponding reply EHS (from the
    device) should also be returned to the user space.  bsg_job->request
    and bsg_job->reply allow us to pass and return EHS from/back to
    userspace.

Compared to normal/legacy RPMB, the advantages of advanced RPMB are:

 1. The data length in the Advanced RPMB data read/write command can be
    larger than 4KB. For the legacy RPMB, the data length in a single RPMB
    data transfer is 256 bytes.

 2. All of the advanced RPMB operations will be a single command. For
    legacy RPMB, take the read write-counter value as an example, you need
    two commands (first SECURITY PROTOCOL OUT, then second SECURITY
    PROTOCOL IN).

Signed-off-by: default avatarBean Huo <beanhuo@micron.com>
Reviewed-by: default avatarAvri Altman <avri.altman@wdc.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent a4b1c9b9
Loading
Loading
Loading
Loading
+83 −10
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 */

#include <linux/bsg-lib.h>
#include <linux/dma-mapping.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include "ufs_bsg.h"
@@ -68,6 +69,72 @@ static int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job,
	return 0;
}

static int ufs_bsg_exec_advanced_rpmb_req(struct ufs_hba *hba, struct bsg_job *job)
{
	struct ufs_rpmb_request *rpmb_request = job->request;
	struct ufs_rpmb_reply *rpmb_reply = job->reply;
	struct bsg_buffer *payload = NULL;
	enum dma_data_direction dir;
	struct scatterlist *sg_list;
	int rpmb_req_type;
	int sg_cnt;
	int ret;
	int data_len;

	if (hba->ufs_version < ufshci_version(4, 0) || !hba->dev_info.b_advanced_rpmb_en ||
	    !(hba->capabilities & MASK_EHSLUTRD_SUPPORTED))
		return -EINVAL;

	if (rpmb_request->ehs_req.length != 2 || rpmb_request->ehs_req.ehs_type != 1)
		return -EINVAL;

	rpmb_req_type = be16_to_cpu(rpmb_request->ehs_req.meta.req_resp_type);

	switch (rpmb_req_type) {
	case UFS_RPMB_WRITE_KEY:
	case UFS_RPMB_READ_CNT:
	case UFS_RPMB_PURGE_ENABLE:
		dir = DMA_NONE;
		break;
	case UFS_RPMB_WRITE:
	case UFS_RPMB_SEC_CONF_WRITE:
		dir = DMA_TO_DEVICE;
		break;
	case UFS_RPMB_READ:
	case UFS_RPMB_SEC_CONF_READ:
	case UFS_RPMB_PURGE_STATUS_READ:
		dir = DMA_FROM_DEVICE;
		break;
	default:
		return -EINVAL;
	}

	if (dir != DMA_NONE) {
		payload = &job->request_payload;
		if (!payload || !payload->payload_len || !payload->sg_cnt)
			return -EINVAL;

		sg_cnt = dma_map_sg(hba->host->dma_dev, payload->sg_list, payload->sg_cnt, dir);
		if (unlikely(!sg_cnt))
			return -ENOMEM;
		sg_list = payload->sg_list;
		data_len = payload->payload_len;
	}

	ret = ufshcd_advanced_rpmb_req_handler(hba, &rpmb_request->bsg_request.upiu_req,
				   &rpmb_reply->bsg_reply.upiu_rsp, &rpmb_request->ehs_req,
				   &rpmb_reply->ehs_rsp, sg_cnt, sg_list, dir);

	if (dir != DMA_NONE) {
		dma_unmap_sg(hba->host->dma_dev, payload->sg_list, payload->sg_cnt, dir);

		if (!ret)
			rpmb_reply->bsg_reply.reply_payload_rcv_len = data_len;
	}

	return ret;
}

static int ufs_bsg_request(struct bsg_job *job)
{
	struct ufs_bsg_request *bsg_request = job->request;
@@ -75,10 +142,11 @@ static int ufs_bsg_request(struct bsg_job *job)
	struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent));
	struct uic_command uc = {};
	int msgcode;
	uint8_t *desc_buff = NULL;
	uint8_t *buff = NULL;
	int desc_len = 0;
	enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP;
	int ret;
	bool rpmb = false;

	bsg_reply->reply_payload_rcv_len = 0;

@@ -88,8 +156,7 @@ static int ufs_bsg_request(struct bsg_job *job)
	switch (msgcode) {
	case UPIU_TRANSACTION_QUERY_REQ:
		desc_op = bsg_request->upiu_req.qr.opcode;
		ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff,
						&desc_len, desc_op);
		ret = ufs_bsg_alloc_desc_buffer(hba, job, &buff, &desc_len, desc_op);
		if (ret)
			goto out;
		fallthrough;
@@ -97,25 +164,31 @@ static int ufs_bsg_request(struct bsg_job *job)
	case UPIU_TRANSACTION_TASK_REQ:
		ret = ufshcd_exec_raw_upiu_cmd(hba, &bsg_request->upiu_req,
					       &bsg_reply->upiu_rsp, msgcode,
					       desc_buff, &desc_len, desc_op);
					       buff, &desc_len, desc_op);
		if (ret)
			dev_err(hba->dev, "exe raw upiu: error code %d\n", ret);
		else if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len)
		else if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len) {
			bsg_reply->reply_payload_rcv_len =
				sg_copy_from_buffer(job->request_payload.sg_list,
						    job->request_payload.sg_cnt,
						    desc_buff, desc_len);
						    buff, desc_len);
		}
		break;
	case UPIU_TRANSACTION_UIC_CMD:
		memcpy(&uc, &bsg_request->upiu_req.uc, UIC_CMD_SIZE);
		ret = ufshcd_send_uic_cmd(hba, &uc);
		if (ret)
			dev_err(hba->dev,
				"send uic cmd: error code %d\n", ret);
			dev_err(hba->dev, "send uic cmd: error code %d\n", ret);

		memcpy(&bsg_reply->upiu_rsp.uc, &uc, UIC_CMD_SIZE);

		break;
	case UPIU_TRANSACTION_ARPMB_CMD:
		rpmb = true;
		ret = ufs_bsg_exec_advanced_rpmb_req(hba, job);
		if (ret)
			dev_err(hba->dev, "ARPMB OP failed: error code  %d\n", ret);
		break;
	default:
		ret = -ENOTSUPP;
		dev_err(hba->dev, "unsupported msgcode 0x%x\n", msgcode);
@@ -125,9 +198,9 @@ static int ufs_bsg_request(struct bsg_job *job)

out:
	ufshcd_rpm_put_sync(hba);
	kfree(desc_buff);
	kfree(buff);
	bsg_reply->result = ret;
	job->reply_len = sizeof(struct ufs_bsg_reply);
	job->reply_len = !rpmb ? sizeof(struct ufs_bsg_reply) : sizeof(struct ufs_rpmb_reply);
	/* complete the job here only if no error */
	if (ret == 0)
		bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
+103 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@
/* Query request timeout */
#define QUERY_REQ_TIMEOUT 1500 /* 1.5 seconds */

/* Advanced RPMB request timeout */
#define ADVANCED_RPMB_REQ_TIMEOUT  3000 /* 3 seconds */

/* Task management command timeout */
#define TM_CMD_TIMEOUT	100 /* msecs */

@@ -2954,6 +2957,12 @@ ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
		dev_err(hba->dev, "%s: Reject UPIU not fully implemented\n",
				__func__);
		break;
	case UPIU_TRANSACTION_RESPONSE:
		if (hba->dev_cmd.type != DEV_CMD_TYPE_RPMB) {
			err = -EINVAL;
			dev_err(hba->dev, "%s: unexpected response %x\n", __func__, resp);
		}
		break;
	default:
		err = -EINVAL;
		dev_err(hba->dev, "%s: Invalid device management cmd response: %x\n",
@@ -7006,6 +7015,100 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
	return err;
}

/**
 * ufshcd_advanced_rpmb_req_handler - handle advanced RPMB request
 * @hba:	per adapter instance
 * @req_upiu:	upiu request
 * @rsp_upiu:	upiu reply
 * @req_ehs:	EHS field which contains Advanced RPMB Request Message
 * @rsp_ehs:	EHS field which returns Advanced RPMB Response Message
 * @sg_cnt:	The number of sg lists actually used
 * @sg_list:	Pointer to SG list when DATA IN/OUT UPIU is required in ARPMB operation
 * @dir:	DMA direction
 *
 * Returns zero on success, non-zero on failure
 */
int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *req_upiu,
			 struct utp_upiu_req *rsp_upiu, struct ufs_ehs *req_ehs,
			 struct ufs_ehs *rsp_ehs, int sg_cnt, struct scatterlist *sg_list,
			 enum dma_data_direction dir)
{
	DECLARE_COMPLETION_ONSTACK(wait);
	const u32 tag = hba->reserved_slot;
	struct ufshcd_lrb *lrbp;
	int err = 0;
	int result;
	u8 upiu_flags;
	u8 *ehs_data;
	u16 ehs_len;

	/* Protects use of hba->reserved_slot. */
	ufshcd_hold(hba, false);
	mutex_lock(&hba->dev_cmd.lock);
	down_read(&hba->clk_scaling_lock);

	lrbp = &hba->lrb[tag];
	WARN_ON(lrbp->cmd);
	lrbp->cmd = NULL;
	lrbp->task_tag = tag;
	lrbp->lun = UFS_UPIU_RPMB_WLUN;

	lrbp->intr_cmd = true;
	ufshcd_prepare_lrbp_crypto(NULL, lrbp);
	hba->dev_cmd.type = DEV_CMD_TYPE_RPMB;

	/* Advanced RPMB starts from UFS 4.0, so its command type is UTP_CMD_TYPE_UFS_STORAGE */
	lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;

	ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, dir, 2);

	/* update the task tag and LUN in the request upiu */
	req_upiu->header.dword_0 |= cpu_to_be32(upiu_flags << 16 | UFS_UPIU_RPMB_WLUN << 8 | tag);

	/* copy the UPIU(contains CDB) request as it is */
	memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr));
	/* Copy EHS, starting with byte32, immediately after the CDB package */
	memcpy(lrbp->ucd_req_ptr + 1, req_ehs, sizeof(*req_ehs));

	if (dir != DMA_NONE && sg_list)
		ufshcd_sgl_to_prdt(hba, lrbp, sg_cnt, sg_list);

	memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));

	hba->dev_cmd.complete = &wait;

	ufshcd_send_command(hba, tag);

	err = ufshcd_wait_for_dev_cmd(hba, lrbp, ADVANCED_RPMB_REQ_TIMEOUT);

	if (!err) {
		/* Just copy the upiu response as it is */
		memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
		/* Get the response UPIU result */
		result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);

		ehs_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) >> 24;
		/*
		 * Since the bLength in EHS indicates the total size of the EHS Header and EHS Data
		 * in 32 Byte units, the value of the bLength Request/Response for Advanced RPMB
		 * Message is 02h
		 */
		if (ehs_len == 2 && rsp_ehs) {
			/*
			 * ucd_rsp_ptr points to a buffer with a length of 512 bytes
			 * (ALIGNED_UPIU_SIZE = 512), and the EHS data just starts from byte32
			 */
			ehs_data = (u8 *)lrbp->ucd_rsp_ptr + EHS_OFFSET_IN_RESPONSE;
			memcpy(rsp_ehs, ehs_data, ehs_len * 32);
		}
	}

	up_read(&hba->clk_scaling_lock);
	mutex_unlock(&hba->dev_cmd.lock);
	ufshcd_release(hba);
	return err ? : result;
}

/**
 * ufshcd_eh_device_reset_handler() - Reset a single logical unit.
 * @cmd: SCSI command pointer
+45 −1
Original line number Diff line number Diff line
@@ -14,10 +14,27 @@
 */

#define UFS_CDB_SIZE	16
#define UPIU_TRANSACTION_UIC_CMD 0x1F
/* uic commands are 4DW long, per UFSHCI V2.1 paragraph 5.6.1 */
#define UIC_CMD_SIZE (sizeof(__u32) * 4)

enum ufs_bsg_msg_code {
	UPIU_TRANSACTION_UIC_CMD = 0x1F,
	UPIU_TRANSACTION_ARPMB_CMD,
};

/* UFS RPMB Request Message Types */
enum ufs_rpmb_op_type {
	UFS_RPMB_WRITE_KEY		= 0x01,
	UFS_RPMB_READ_CNT		= 0x02,
	UFS_RPMB_WRITE			= 0x03,
	UFS_RPMB_READ			= 0x04,
	UFS_RPMB_READ_RESP		= 0x05,
	UFS_RPMB_SEC_CONF_WRITE		= 0x06,
	UFS_RPMB_SEC_CONF_READ		= 0x07,
	UFS_RPMB_PURGE_ENABLE		= 0x08,
	UFS_RPMB_PURGE_STATUS_READ	= 0x09,
};

/**
 * struct utp_upiu_header - UPIU header structure
 * @dword_0: UPIU header DW-0
@@ -79,6 +96,23 @@ struct utp_upiu_req {
	};
};

struct ufs_arpmb_meta {
	__u16	req_resp_type;
	__u8	nonce[16];
	__u32	write_counter;
	__u16	addr_lun;
	__u16	block_count;
	__u16	result;
} __attribute__((__packed__));

struct ufs_ehs {
	__u8	length;
	__u8	ehs_type;
	__u16	ehssub_type;
	struct ufs_arpmb_meta meta;
	__u8	mac_key[32];
} __attribute__((__packed__));

/* request (CDB) structure of the sg_io_v4 */
struct ufs_bsg_request {
	__u32 msgcode;
@@ -102,4 +136,14 @@ struct ufs_bsg_reply {

	struct utp_upiu_req upiu_rsp;
};

struct ufs_rpmb_request {
	struct ufs_bsg_request bsg_request;
	struct ufs_ehs ehs_req;
};

struct ufs_rpmb_reply {
	struct ufs_bsg_reply bsg_reply;
	struct ufs_ehs ehs_rsp;
};
#endif /* UFS_BSG_H */
+5 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@
 */
#define UFS_WB_EXCEED_LIFETIME		0x0B

/*
 * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU request/response packet
 */
#define EHS_OFFSET_IN_RESPONSE 32

/* Well known logical unit id in LUN field of UPIU */
enum {
	UFS_UPIU_REPORT_LUNS_WLUN	= 0x81,
+5 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ struct ufs_hba;
enum dev_cmd_type {
	DEV_CMD_TYPE_NOP		= 0x0,
	DEV_CMD_TYPE_QUERY		= 0x1,
	DEV_CMD_TYPE_RPMB		= 0x2,
};

enum ufs_event_type {
@@ -1201,7 +1202,10 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
			     int msgcode,
			     u8 *desc_buff, int *buff_len,
			     enum query_opcode desc_op);

int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *req_upiu,
				     struct utp_upiu_req *rsp_upiu, struct ufs_ehs *ehs_req,
				     struct ufs_ehs *ehs_rsp, int sg_cnt,
				     struct scatterlist *sg_list, enum dma_data_direction dir);
int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable);
int ufshcd_wb_toggle_buf_flush(struct ufs_hba *hba, bool enable);
int ufshcd_suspend_prepare(struct device *dev);
Loading