From: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
To: jorge.ramirez@oss.qualcomm.com, dlechner@baylibre.com,
trini@konsulko.com, jens.wiklander@linaro.org,
ilias.apalodimas@linaro.org, peng.fan@nxp.com,
jh80.chung@samsung.com, tanmay.kathpalia@altera.com,
neil.armstrong@linaro.org, bhupesh.linux@gmail.com,
n-francis@ti.com, xypron.glpk@gmx.de, s-vadapalli@ti.com,
michal.simek@amd.com, jbx6244@gmail.com,
marek.vasut+renesas@mailbox.org, sumit.garg@oss.qualcomm.com,
jens.wiklander@oss.qualcomm.com
Cc: u-boot@lists.u-boot-project.org
Subject: [PATCH v5 3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL
Date: Fri, 18 Sep 2026 22:06:35 +0200 [thread overview]
Message-ID: <20260918200641.2286077-4-jorge.ramirez@oss.qualcomm.com> (raw)
In-Reply-To: <20260918200641.2286077-1-jorge.ramirez@oss.qualcomm.com>
OP-TEE secure storage (CFG_RPMB_FS) needs RPMB, but on UFS-only platforms
with no eMMC the OP-TEE RPMB supplicant cannot reach the device's RPMB
Well-Known LUN: UFS exposes RPMB through SCSI SECURITY PROTOCOL IN/OUT
rather than an eMMC-style partition. Add that transport so RPMB-backed
secure storage works on UFS platforms.
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
---
drivers/ufs/Kconfig | 10 +++
drivers/ufs/Makefile | 1 +
drivers/ufs/ufs-rpmb.c | 167 +++++++++++++++++++++++++++++++++++++++
drivers/ufs/ufs-uclass.c | 6 +-
drivers/ufs/ufs.h | 7 ++
include/scsi.h | 2 +
include/ufs.h | 4 +
7 files changed, 194 insertions(+), 3 deletions(-)
create mode 100644 drivers/ufs/ufs-rpmb.c
diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
index 6dffdc00b32..a0067b1f203 100644
--- a/drivers/ufs/Kconfig
+++ b/drivers/ufs/Kconfig
@@ -92,4 +92,14 @@ config UFS_TI_J721E
This selects the glue layer driver for Cadence controller
present on TI's J721E devices.
+config SUPPORT_UFS_RPMB
+ bool "Enable UFS RPMB (Replay Protected Memory Block) support"
+ depends on UFS && OPTEE && !SUPPORT_EMMC_RPMB
+ help
+ Route OP-TEE RPMB requests to the UFS RPMB Well-Known LUN using
+ SCSI SECURITY PROTOCOL IN/OUT commands. Required for OP-TEE secure
+ storage (CFG_RPMB_FS) on UFS-based platforms that have no eMMC.
+ The OP-TEE supplicant handles a single RPMB transport, so this is
+ mutually exclusive with the eMMC RPMB supplicant (SUPPORT_EMMC_RPMB).
+
endmenu
diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile
index e7f3c1d30c4..d60440ca119 100644
--- a/drivers/ufs/Makefile
+++ b/drivers/ufs/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o
obj-$(CONFIG_UFS_RENESAS_GEN5) += ufs-renesas-rcar-gen5.o
obj-$(CONFIG_UFS_ROCKCHIP) += ufs-rockchip.o
obj-$(CONFIG_UFS_TI_J721E) += ti-j721e-ufs.o
+obj-$(CONFIG_SUPPORT_UFS_RPMB) += ufs-rpmb.o
diff --git a/drivers/ufs/ufs-rpmb.c b/drivers/ufs/ufs-rpmb.c
new file mode 100644
index 00000000000..e6e0b2aec33
--- /dev/null
+++ b/drivers/ufs/ufs-rpmb.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <scsi.h>
+#include <ufs.h>
+#include <asm/cache.h>
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "ufs.h"
+
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+#define RPMB_FRAME_SIZE 512
+#define RPMB_FRAME_REQ_OFFSET 510
+
+#define UFS_RPMB_UA_RETRIES 3
+
+#define SEC_PROTOCOL_UFS 0xEC
+#define UFS_RPMB_SEC_PROTOCOL_ID 0x01
+
+static u8 ufs_rpmb_sense_key(const struct scsi_cmd *pccb)
+{
+ const u8 *sense = pccb->sense_buf;
+
+ if (!pccb->sensedatalen)
+ return SENSE_NO_SENSE;
+
+ /* fixed (0x70/0x71) vs descriptor (0x72/0x73) sense format */
+ if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73)
+ return sense[1] & 0xf;
+
+ return sense[2] & 0xf;
+}
+
+static int ufs_rpmb_secprot(struct udevice *scsi_dev, unsigned int region,
+ u8 opcode, void *buf, unsigned int nframes,
+ enum dma_data_direction dir)
+{
+ u16 spsp = (region << 8) | UFS_RPMB_SEC_PROTOCOL_ID;
+ u32 len = nframes * RPMB_FRAME_SIZE;
+ struct scsi_cmd pccb;
+ void *dma_buf = buf;
+ void *bounce = NULL;
+ int retries;
+ int ret = 0;
+
+ if (!IS_ALIGNED((uintptr_t)buf, ARCH_DMA_MINALIGN)) {
+ bounce = memalign(ARCH_DMA_MINALIGN,
+ ALIGN(len, ARCH_DMA_MINALIGN));
+ if (!bounce)
+ return -ENOMEM;
+ dma_buf = bounce;
+ if (dir == DMA_TO_DEVICE)
+ memcpy(bounce, buf, len);
+ }
+
+ memset(&pccb, 0, sizeof(pccb));
+ pccb.lun = UFS_UPIU_RPMB_WLUN;
+ pccb.cmd[0] = opcode;
+ pccb.cmd[1] = SEC_PROTOCOL_UFS;
+ put_unaligned_be16(spsp, &pccb.cmd[2]);
+ pccb.cmd[4] = 0;
+ pccb.cmd[5] = 0;
+ put_unaligned_be32(len, &pccb.cmd[6]);
+ pccb.cmd[10] = 0;
+ pccb.cmd[11] = 0;
+ pccb.cmdlen = 12;
+ pccb.pdata = dma_buf;
+ pccb.datalen = len;
+ pccb.dma_dir = dir;
+
+ for (retries = UFS_RPMB_UA_RETRIES; retries > 0; retries--) {
+ ret = scsi_exec(scsi_dev, &pccb);
+ if (!ret)
+ break;
+
+ /*
+ * A power-on or reset UNIT ATTENTION is a transient
+ * condition the device clears once it has been reported,
+ * so retry it. Any other failure is final: stop and
+ * report it.
+ */
+ if (ufs_rpmb_sense_key(&pccb) != SENSE_UNIT_ATTENTION)
+ break;
+ }
+
+ if (bounce) {
+ if (!ret && dir == DMA_FROM_DEVICE)
+ memcpy(buf, bounce, len);
+
+ free(bounce);
+ }
+
+ return ret;
+}
+
+static int ufs_rpmb_send(struct udevice *scsi_dev, unsigned int region,
+ void *frames, unsigned int nframes)
+{
+ return ufs_rpmb_secprot(scsi_dev, region, SCSI_SECURITY_PROTOCOL_OUT,
+ frames, nframes, DMA_TO_DEVICE);
+}
+
+static int ufs_rpmb_recv(struct udevice *scsi_dev, unsigned int region,
+ void *frames, unsigned int nframes)
+{
+ return ufs_rpmb_secprot(scsi_dev, region, SCSI_SECURITY_PROTOCOL_IN,
+ frames, nframes, DMA_FROM_DEVICE);
+}
+
+int ufs_rpmb_route_frames(struct udevice *scsi_dev, unsigned int region,
+ void *req, unsigned long reqlen, void *rsp,
+ unsigned long rsplen)
+{
+ unsigned int n_req = reqlen / RPMB_FRAME_SIZE;
+ unsigned int n_rsp = rsplen / RPMB_FRAME_SIZE;
+ u16 request;
+ int ret;
+
+ if (!scsi_dev || reqlen % RPMB_FRAME_SIZE ||
+ rsplen % RPMB_FRAME_SIZE || !n_req)
+ return -EINVAL;
+
+ request = get_unaligned_be16((u8 *)req + RPMB_FRAME_REQ_OFFSET);
+
+ switch (request) {
+ case RPMB_REQ_KEY:
+ case RPMB_REQ_WRITE_DATA: {
+ u8 status_frame[RPMB_FRAME_SIZE];
+
+ ret = ufs_rpmb_send(scsi_dev, region, req, n_req);
+ if (ret)
+ return ret;
+
+ memset(status_frame, 0, sizeof(status_frame));
+ put_unaligned_be16(RPMB_REQ_STATUS,
+ &status_frame[RPMB_FRAME_REQ_OFFSET]);
+ ret = ufs_rpmb_send(scsi_dev, region, status_frame, 1);
+ if (ret)
+ return ret;
+
+ if (n_rsp < 1)
+ return -EINVAL;
+
+ return ufs_rpmb_recv(scsi_dev, region, rsp, 1);
+ }
+ case RPMB_REQ_WCOUNTER:
+ case RPMB_REQ_READ_DATA:
+ ret = ufs_rpmb_send(scsi_dev, region, req, 1);
+ if (ret)
+ return ret;
+
+ if (n_rsp < 1)
+ return -EINVAL;
+
+ return ufs_rpmb_recv(scsi_dev, region, rsp, n_rsp);
+ default:
+ debug("ufs-rpmb: unsupported request 0x%x\n", request);
+ return -EINVAL;
+ }
+}
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 3d96a05281c..2ca4fb2a020 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -1458,9 +1458,9 @@ static int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_
* ufshcd_read_desc_param - read the specified descriptor parameter
*
*/
-static int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id,
- int desc_index, u8 param_offset,
- u8 *param_read_buf, u8 param_size)
+int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id,
+ int desc_index, u8 param_offset,
+ u8 *param_read_buf, u8 param_size)
{
int ret;
u8 *desc_buf;
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 0f6c93fbce7..9f55735602b 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -10,6 +10,9 @@
struct udevice;
#define UFS_CDB_SIZE 16
+
+#define UFS_UPIU_RPMB_WLUN 0xC4
+
#define UPIU_TRANSACTION_UIC_CMD 0x1F
#define UIC_CMD_SIZE (sizeof(u32) * 4)
#define RESPONSE_UPIU_SENSE_DATA_LENGTH 18
@@ -809,4 +812,8 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
int ufshcd_probe(struct udevice *dev, struct ufs_hba_ops *hba_ops);
+int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id,
+ int desc_index, u8 param_offset, u8 *param_read_buf,
+ u8 param_size);
+
#endif
diff --git a/include/scsi.h b/include/scsi.h
index 2520a8b8fe6..cc79b6dd4c4 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -183,6 +183,8 @@ struct scsi_cmd {
#define SCSI_WRITE_LONG 0x3F /* Write Long (O) */
#define SCSI_WRITE_SAME 0x41 /* Write Same (O) */
#define SCSI_UNMAP 0x42 /* Write 10-Byte (MANDATORY) */
+#define SCSI_SECURITY_PROTOCOL_IN 0xA2 /* Security Protocol In (O) */
+#define SCSI_SECURITY_PROTOCOL_OUT 0xB5 /* Security Protocol Out (O) */
/**
* enum scsi_cmd_phase - current phase of the SCSI protocol
diff --git a/include/ufs.h b/include/ufs.h
index f6e27d90e43..1bec3ce73a4 100644
--- a/include/ufs.h
+++ b/include/ufs.h
@@ -20,4 +20,8 @@ int ufs_probe(void);
*/
int ufs_probe_dev(int index);
+int ufs_rpmb_route_frames(struct udevice *scsi_dev, unsigned int region,
+ void *req, unsigned long reqlen, void *rsp,
+ unsigned long rsplen);
+
#endif
--
2.54.0
next prev parent reply other threads:[~2026-09-18 20:07 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-18 20:06 [PATCH v5 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
2026-09-18 20:06 ` [PATCH v5 1/6] ufs: decode string descriptors as UTF-16 big-endian Jorge Ramirez-Ortiz
2026-09-18 20:06 ` [PATCH v5 2/6] ufs: surface sense data from ufs_scsi_exec() Jorge Ramirez-Ortiz
2026-10-02 11:16 ` Ilias Apalodimas
2026-10-13 17:48 ` Jorge Ramirez
2026-10-13 17:51 ` Tom Rini
2026-09-18 20:06 ` Jorge Ramirez-Ortiz [this message]
2026-09-29 3:29 ` [PATCH v5 3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL Stanley Jhu
2026-09-18 20:06 ` [PATCH v5 4/6] ufs: derive the per-region RPMB CID and size for OP-TEE Jorge Ramirez-Ortiz
2026-09-18 20:06 ` [PATCH v5 5/6] optee: rename rpmb.c to rpmb_emmc.c Jorge Ramirez-Ortiz
2026-10-02 11:14 ` Ilias Apalodimas
2026-09-18 20:06 ` [PATCH v5 6/6] optee: implement the RPMB subsystem interface for UFS Jorge Ramirez-Ortiz
2026-10-02 11:14 ` Ilias Apalodimas
2026-09-18 20:15 ` [PATCH v5 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez
2026-09-24 17:50 ` Macpaul Lin (林智斌)
2026-09-25 9:59 ` Ilias Apalodimas
2026-09-26 20:41 ` Jorge Ramirez
2026-09-26 21:08 ` Jorge Ramirez
2026-09-28 7:32 ` Jens Wiklander
2026-09-28 10:02 ` Ilias Apalodimas
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260918200641.2286077-4-jorge.ramirez@oss.qualcomm.com \
--to=jorge.ramirez@oss.qualcomm.com \
--cc=bhupesh.linux@gmail.com \
--cc=dlechner@baylibre.com \
--cc=ilias.apalodimas@linaro.org \
--cc=jbx6244@gmail.com \
--cc=jens.wiklander@linaro.org \
--cc=jens.wiklander@oss.qualcomm.com \
--cc=jh80.chung@samsung.com \
--cc=marek.vasut+renesas@mailbox.org \
--cc=michal.simek@amd.com \
--cc=n-francis@ti.com \
--cc=neil.armstrong@linaro.org \
--cc=peng.fan@nxp.com \
--cc=s-vadapalli@ti.com \
--cc=sumit.garg@oss.qualcomm.com \
--cc=tanmay.kathpalia@altera.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.u-boot-project.org \
--cc=xypron.glpk@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.