* [PATCH v6 1/6] ufs: decode string descriptors as UTF-16 big-endian
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 2/6] ufs: surface sense data from ufs_scsi_exec() Jorge Ramirez-Ortiz
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
UFS string descriptors are UTF-16 big-endian (JESD220), but
ufshcd_read_string_desc() decoded them as host-endian and so left
dev_desc->model blank.
Give utf16_to_utf8() the source byte order and decode UFS descriptors as
big-endian, matching the kernel's utf16s_to_utf8s(). The EFI file-path
callers consume little-endian UEFI strings and are marked accordingly.
Fixes: 067c1b033282 ("ufs: Call ufs_scsi_bind() from uclass .post_bind")
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
Acked-by: Neil Armstrong <neil.armstrong@linaro.org>
---
drivers/ufs/ufs-uclass.c | 7 ++++---
include/charset.h | 17 ++++++++++++++++-
lib/charset.c | 15 ++++++++++++++-
lib/efi_loader/efi_file.c | 4 ++--
4 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 6ef01f8e4aa..21aed9f97bd 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -1767,11 +1767,12 @@ static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
}
/*
- * the descriptor contains string in UTF16 format
- * we need to convert to utf-8 so it can be displayed
+ * the descriptor contains a big-endian UTF-16 string, convert
+ * it to utf-8 so it can be displayed
*/
utf16_to_utf8(buff_ascii,
- (uint16_t *)&buf[QUERY_DESC_HDR_SIZE], ascii_len);
+ (uint16_t *)&buf[QUERY_DESC_HDR_SIZE], ascii_len,
+ UTF16_BIG_ENDIAN);
/* replace non-printable or non-ASCII characters with spaces */
for (i = 0; i < ascii_len; i++)
diff --git a/include/charset.h b/include/charset.h
index 348bad5883a..79d0ae1ef49 100644
--- a/include/charset.h
+++ b/include/charset.h
@@ -13,6 +13,19 @@
#define MAX_UTF8_PER_UTF16 3
+/**
+ * enum utf16_endian - byte order of a UTF-16 string
+ *
+ * @UTF16_HOST_ENDIAN: code units are in host byte order
+ * @UTF16_LITTLE_ENDIAN: code units are little-endian
+ * @UTF16_BIG_ENDIAN: code units are big-endian
+ */
+enum utf16_endian {
+ UTF16_HOST_ENDIAN,
+ UTF16_LITTLE_ENDIAN,
+ UTF16_BIG_ENDIAN,
+};
+
/*
* codepage_437 - Unicode to codepage 437 translation table
*/
@@ -299,9 +312,11 @@ size_t u16_strlcat(u16 *dest, const u16 *src, size_t count);
* @dest: the destination buffer to write the utf8 characters
* @src: the source utf16 string
* @size: the number of utf16 characters to convert
+ * @endian: byte order of the code units in 'src'
* Return: the pointer to the first unwritten byte in 'dest'
*/
-uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
+u8 *utf16_to_utf8(u8 *dest, const u16 *src, size_t size,
+ enum utf16_endian endian);
/**
* utf_to_cp() - translate Unicode code point to 8bit codepage
diff --git a/lib/charset.c b/lib/charset.c
index 182c92a50c4..8ccac4048c0 100644
--- a/lib/charset.c
+++ b/lib/charset.c
@@ -5,6 +5,7 @@
* Copyright (c) 2017 Rob Clark
*/
+#include <asm/byteorder.h>
#include <charset.h>
#include <capitalization.h>
#include <cp437.h>
@@ -458,13 +459,25 @@ size_t u16_strlcat(u16 *dest, const u16 *src, size_t count)
}
/* Convert UTF-16 to UTF-8. */
-uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
+u8 *utf16_to_utf8(u8 *dest, const u16 *src, size_t size,
+ enum utf16_endian endian)
{
uint32_t code_high = 0;
while (size--) {
uint32_t code = *src++;
+ switch (endian) {
+ case UTF16_LITTLE_ENDIAN:
+ code = le16_to_cpu(code);
+ break;
+ case UTF16_BIG_ENDIAN:
+ code = be16_to_cpu(code);
+ break;
+ case UTF16_HOST_ENDIAN:
+ break;
+ }
+
if (code_high) {
if (code >= 0xDC00 && code <= 0xDFFF) {
/* Surrogate pair. */
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 19b43c4a625..7dee57d594a 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -184,7 +184,7 @@ static struct efi_file_handle *file_open(struct file_system *fs,
int flen = 0;
if (file_name) {
- utf16_to_utf8((u8 *)f0, file_name, 1);
+ utf16_to_utf8((u8 *)f0, file_name, 1, UTF16_LITTLE_ENDIAN);
flen = u16_strlen(file_name);
}
@@ -216,7 +216,7 @@ static struct efi_file_handle *file_open(struct file_system *fs,
*p++ = '/';
}
- utf16_to_utf8((u8 *)p, file_name, flen);
+ utf16_to_utf8((u8 *)p, file_name, flen, UTF16_LITTLE_ENDIAN);
if (sanitize_path(fh->path))
goto error;
base-commit: 8d534cec0cc0109a37c0ded8854da03f1526dc0e
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 2/6] ufs: surface sense data from ufs_scsi_exec()
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 1/6] ufs: decode string descriptors as UTF-16 big-endian Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL Jorge Ramirez-Ortiz
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
On a CHECK CONDITION the UFS device returns SCSI sense data inline in the
RESPONSE UPIU, but ufs_scsi_exec() discarded it and reported every
failure as a bare -EINVAL, leaving callers unable to tell a transient
condition such as a power-on UNIT ATTENTION from a hard error.
Preserve the sense data and SCSI status in the scsi_cmd. No caller
inspects them yet, so behaviour is unchanged.
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
Acked-by: Neil Armstrong <neil.armstrong@linaro.org>
---
drivers/ufs/ufs-uclass.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index 21aed9f97bd..3d96a05281c 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -1657,6 +1657,22 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb)
ufshcd_cache_flush(req_desc, sizeof(*req_desc));
}
+/*
+ * On a CHECK CONDITION the sense data is returned inline in the RESPONSE
+ * UPIU; copy it into the SCSI command so callers can inspect the sense key
+ * (for example to tell a transient UNIT ATTENTION from a hard error).
+ */
+static void ufs_scsi_copy_sense(struct ufs_hba *hba, struct scsi_cmd *pccb)
+{
+ u16 len = be16_to_cpu(hba->ucd_rsp_ptr->sr.sense_data_len);
+
+ if (len > RESPONSE_UPIU_SENSE_DATA_LENGTH)
+ len = RESPONSE_UPIU_SENSE_DATA_LENGTH;
+
+ memcpy(pccb->sense_buf, hba->ucd_rsp_ptr->sr.sense_data, len);
+ pccb->sensedatalen = len;
+}
+
static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb)
{
struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent);
@@ -1664,6 +1680,9 @@ static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb)
int ocs, result = 0;
u8 scsi_status;
+ pccb->status = 0;
+ pccb->sensedatalen = 0;
+
ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, pccb->dma_dir);
ufshcd_prepare_utp_scsi_cmd_upiu(hba, pccb, upiu_flags);
prepare_prdt_table(hba, pccb);
@@ -1683,8 +1702,12 @@ static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb)
result = ufshcd_get_rsp_upiu_result(hba->ucd_rsp_ptr);
scsi_status = result & MASK_SCSI_STATUS;
- if (scsi_status)
+ if (scsi_status) {
+ pccb->status = scsi_status;
+ if (scsi_status == S_CHECK_COND)
+ ufs_scsi_copy_sense(hba, pccb);
return -EINVAL;
+ }
break;
case UPIU_TRANSACTION_REJECT_UPIU:
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 1/6] ufs: decode string descriptors as UTF-16 big-endian Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 2/6] ufs: surface sense data from ufs_scsi_exec() Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 4/6] ufs: derive the per-region RPMB CID and size for OP-TEE Jorge Ramirez-Ortiz
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
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.
Reported-by: Stanley Jhu <stanleyjhu@google.com>
Closes: https://lore.kernel.org/u-boot/20260929032920.1391393-1-stanleyjhu@google.com/
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
---
drivers/ufs/Kconfig | 10 +++
drivers/ufs/Makefile | 1 +
drivers/ufs/ufs-rpmb.c | 186 +++++++++++++++++++++++++++++++++++++++
drivers/ufs/ufs-uclass.c | 6 +-
drivers/ufs/ufs.h | 7 ++
include/scsi.h | 2 +
include/ufs.h | 4 +
7 files changed, 213 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..97b1c0f82f2
--- /dev/null
+++ b/drivers/ufs/ufs-rpmb.c
@@ -0,0 +1,186 @@
+// 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
+
+/* ASC 29h: POWER ON, RESET, OR BUS DEVICE RESET OCCURRED (any ASCQ) */
+#define UFS_RPMB_UA_ASC_POR 0x29
+
+#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 u8 ufs_rpmb_sense_asc(const struct scsi_cmd *pccb)
+{
+ const u8 *sense = pccb->sense_buf;
+
+ if (!pccb->sensedatalen)
+ return 0;
+
+ /* fixed (0x70/0x71) vs descriptor (0x72/0x73) sense format */
+ if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73)
+ return pccb->sensedatalen > 2 ? sense[2] : 0;
+
+ return pccb->sensedatalen > 12 ? sense[12] : 0;
+}
+
+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 (ASC 29h, any ASCQ) is a
+ * transient condition the device clears once it has been
+ * reported, so retry it. Any other failure - including other
+ * UNIT ATTENTION conditions such as 2Ah MODE PARAMETERS CHANGED
+ * - is final: stop and report it.
+ */
+ if (ufs_rpmb_sense_key(&pccb) != SENSE_UNIT_ATTENTION ||
+ ufs_rpmb_sense_asc(&pccb) != UFS_RPMB_UA_ASC_POR)
+ 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 93561749b7f..439a7f4fede 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -181,6 +181,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
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 4/6] ufs: derive the per-region RPMB CID and size for OP-TEE
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
` (2 preceding siblings ...)
2026-10-18 20:50 ` [PATCH v6 3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 5/6] optee: rename rpmb.c to rpmb_emmc.c Jorge Ramirez-Ortiz
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
OP-TEE derives its RPMB authentication key from a device identifier, so
U-Boot must supply the exact identifier the Linux kernel uses; otherwise
the keys differ and OP-TEE cannot access RPMB storage provisioned under
Linux, or vice versa. The RPMB probe also needs each region's size and
reliable-write count to enumerate the regions. Provide both so secure
storage stays interoperable between U-Boot and Linux on the same device.
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
---
drivers/ufs/Kconfig | 7 +-
drivers/ufs/ufs-rpmb.c | 181 +++++++++++++++++++++++++++++++++++++++++
include/ufs.h | 6 ++
3 files changed, 192 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
index a0067b1f203..648b58b46e4 100644
--- a/drivers/ufs/Kconfig
+++ b/drivers/ufs/Kconfig
@@ -95,11 +95,14 @@ config UFS_TI_J721E
config SUPPORT_UFS_RPMB
bool "Enable UFS RPMB (Replay Protected Memory Block) support"
depends on UFS && OPTEE && !SUPPORT_EMMC_RPMB
+ select BLAKE2
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).
+ BLAKE2 is used to derive the fixed-length RPMB CID that matches the
+ Linux kernel UFS device_id ABI. 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/ufs-rpmb.c b/drivers/ufs/ufs-rpmb.c
index 97b1c0f82f2..d9d35a06439 100644
--- a/drivers/ufs/ufs-rpmb.c
+++ b/drivers/ufs/ufs-rpmb.c
@@ -1,13 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
#include <dm.h>
+#include <hexdump.h>
#include <log.h>
#include <malloc.h>
#include <scsi.h>
#include <ufs.h>
+#include <vsprintf.h>
#include <asm/cache.h>
#include <asm/unaligned.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <u-boot/blake2.h>
#include "ufs.h"
#define RPMB_REQ_KEY 1
@@ -27,6 +30,17 @@
#define SEC_PROTOCOL_UFS 0xEC
#define UFS_RPMB_SEC_PROTOCOL_ID 0x01
+#define GEOMETRY_DESC_RPMB_RW_SIZE 0x17
+
+#define RPMB_UNIT_DESC_LOGICAL_BLK_SIZE 0x0A
+#define RPMB_UNIT_DESC_LOGICAL_BLK_COUNT 0x0B
+#define RPMB_UNIT_DESC_REGION0_SIZE 0x13
+#define RPMB_UNIT_DESC_REGION1_SIZE 0x14
+#define RPMB_UNIT_DESC_REGION2_SIZE 0x15
+#define RPMB_UNIT_DESC_REGION3_SIZE 0x16
+#define UFS_RPMB_LEGACY_SPEC_VER 0x0220
+#define UFS_RPMB_REGION_UNIT_SHIFT 17
+
static u8 ufs_rpmb_sense_key(const struct scsi_cmd *pccb)
{
const u8 *sense = pccb->sense_buf;
@@ -184,3 +198,170 @@ int ufs_rpmb_route_frames(struct udevice *scsi_dev, unsigned int region,
return -EINVAL;
}
}
+
+static int ufs_rpmb_read_geometry(struct udevice *scsi_dev, u8 *rpmb_rw_size)
+{
+ struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent);
+ u8 desc[QUERY_DESC_GEOMETRY_DEF_SIZE];
+ int ret;
+
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0, 0,
+ desc, sizeof(desc));
+ if (ret)
+ return ret;
+
+ *rpmb_rw_size = desc[GEOMETRY_DESC_RPMB_RW_SIZE];
+
+ return 0;
+}
+
+static int ufs_rpmb_read_region_sizes(struct ufs_hba *hba, u16 spec_ver,
+ u8 sizes[UFS_RPMB_NUM_REGIONS])
+{
+ u8 unit[QUERY_DESC_UNIT_DEF_SIZE] = { };
+ int ret;
+
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT,
+ UFS_UPIU_RPMB_WLUN, 0, unit, sizeof(unit));
+ if (ret)
+ return ret;
+
+ memset(sizes, 0, UFS_RPMB_NUM_REGIONS);
+
+ if (spec_ver > UFS_RPMB_LEGACY_SPEC_VER) {
+ sizes[0] = unit[RPMB_UNIT_DESC_REGION0_SIZE];
+ sizes[1] = unit[RPMB_UNIT_DESC_REGION1_SIZE];
+ sizes[2] = unit[RPMB_UNIT_DESC_REGION2_SIZE];
+ sizes[3] = unit[RPMB_UNIT_DESC_REGION3_SIZE];
+ } else {
+ u8 blk_shift = unit[RPMB_UNIT_DESC_LOGICAL_BLK_SIZE];
+ u64 region;
+
+ /*
+ * bLogicalBlockSize is a device-supplied log2 block size;
+ * reject values that would make the shift below undefined.
+ */
+ if (blk_shift >= 64)
+ return -EINVAL;
+
+ region = get_unaligned_be64(unit +
+ RPMB_UNIT_DESC_LOGICAL_BLK_COUNT);
+ region <<= blk_shift;
+ region >>= UFS_RPMB_REGION_UNIT_SHIFT;
+
+ sizes[0] = region > 0xff ? 0xff : region;
+ }
+
+ return 0;
+}
+
+static void ufs_rpmb_string_to_ascii(const u8 *raw, char *out, size_t outsz)
+{
+ int nchars = ((int)raw[QUERY_DESC_LENGTH_OFFSET] - QUERY_DESC_HDR_SIZE);
+ int i, n = 0;
+
+ nchars = nchars > 0 ? nchars / 2 : 0;
+ for (i = 0; i < nchars && n < (int)outsz - 1; i++) {
+ u16 c = get_unaligned_be16(raw + QUERY_DESC_HDR_SIZE + i * 2);
+
+ out[n++] = (c >= 0x20 && c <= 0x7e) ? (char)c : ' ';
+ }
+ out[n] = '\0';
+}
+
+static int ufs_rpmb_build_cid(struct ufs_hba *hba, const u8 *dev_desc,
+ unsigned int region, u8 *cid)
+{
+ char serial_hex[QUERY_DESC_MAX_SIZE * 2 + 1];
+ u16 manf_id, spec_ver, dev_ver, manf_date;
+ u8 serial[QUERY_DESC_MAX_SIZE] = { };
+ char idstr[QUERY_DESC_MAX_SIZE * 3];
+ char model[MAX_MODEL_LEN * 8];
+ u8 raw[QUERY_DESC_MAX_SIZE];
+ u8 blen;
+ int ret;
+
+ manf_date = get_unaligned_be16(dev_desc + DEVICE_DESC_PARAM_MANF_DATE);
+ spec_ver = get_unaligned_be16(dev_desc + DEVICE_DESC_PARAM_SPEC_VER);
+ manf_id = get_unaligned_be16(dev_desc + DEVICE_DESC_PARAM_MANF_ID);
+ dev_ver = get_unaligned_be16(dev_desc + DEVICE_DESC_PARAM_DEV_VER);
+
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_STRING,
+ dev_desc[DEVICE_DESC_PARAM_PRDCT_NAME], 0,
+ raw, sizeof(raw));
+ if (ret)
+ return ret;
+
+ ufs_rpmb_string_to_ascii(raw, model, sizeof(model));
+
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_STRING,
+ dev_desc[DEVICE_DESC_PARAM_SN], 0,
+ raw, sizeof(raw));
+ if (ret)
+ return ret;
+
+ blen = raw[QUERY_DESC_LENGTH_OFFSET];
+ if (blen < QUERY_DESC_HDR_SIZE)
+ return -EINVAL;
+
+ memcpy(serial, raw + QUERY_DESC_HDR_SIZE, blen - QUERY_DESC_HDR_SIZE);
+ /*
+ * The identifier format is an ABI shared with the secure world. The
+ * kernel's ufshcd_create_device_id() hex-encodes the full descriptor
+ * length (header included), so the serial always carries two trailing
+ * zero bytes; reproduce that here so the derived CID matches.
+ */
+ bin2hex(serial_hex, serial, blen);
+ serial_hex[blen * 2] = '\0';
+
+ snprintf(idstr, sizeof(idstr), "%04X-%04X-%s-%s-%04X-%04X-R%u",
+ manf_id, spec_ver, model, serial_hex, dev_ver, manf_date,
+ region);
+
+ if (blake2b(cid, UFS_RPMB_CID_SIZE, idstr, strlen(idstr), NULL, 0))
+ return -EIO;
+
+ return 0;
+}
+
+int ufs_rpmb_get_region_info(struct udevice *scsi_dev, unsigned int region,
+ u8 *size_mult, u8 *rel_wr, u8 *cid)
+{
+ struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent);
+ u8 dev_desc[QUERY_DESC_DEVICE_DEF_SIZE] = { };
+ u8 sizes[UFS_RPMB_NUM_REGIONS];
+ u16 spec_ver;
+ int ret;
+
+ if (region >= UFS_RPMB_NUM_REGIONS)
+ return 0;
+
+ ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_DEVICE, 0, 0,
+ dev_desc, sizeof(dev_desc));
+ if (ret)
+ return ret;
+
+ spec_ver = get_unaligned_be16(dev_desc + DEVICE_DESC_PARAM_SPEC_VER);
+
+ ret = ufs_rpmb_read_region_sizes(hba, spec_ver, sizes);
+ if (ret)
+ return ret;
+
+ if (!sizes[region])
+ return 0;
+
+ ret = ufs_rpmb_read_geometry(scsi_dev, rel_wr);
+ if (ret)
+ return ret;
+
+ if (!*rel_wr)
+ *rel_wr = 1;
+
+ ret = ufs_rpmb_build_cid(hba, dev_desc, region, cid);
+ if (ret)
+ return ret;
+
+ *size_mult = sizes[region];
+
+ return 1;
+}
diff --git a/include/ufs.h b/include/ufs.h
index 1bec3ce73a4..a8b7d370f78 100644
--- a/include/ufs.h
+++ b/include/ufs.h
@@ -20,8 +20,14 @@ int ufs_probe(void);
*/
int ufs_probe_dev(int index);
+#define UFS_RPMB_CID_SIZE 16
+#define UFS_RPMB_NUM_REGIONS 4
+
int ufs_rpmb_route_frames(struct udevice *scsi_dev, unsigned int region,
void *req, unsigned long reqlen, void *rsp,
unsigned long rsplen);
+int ufs_rpmb_get_region_info(struct udevice *scsi_dev, unsigned int region,
+ u8 *size_mult, u8 *rel_wr, u8 *cid);
+
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 5/6] optee: rename rpmb.c to rpmb_emmc.c
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
` (3 preceding siblings ...)
2026-10-18 20:50 ` [PATCH v6 4/6] ufs: derive the per-region RPMB CID and size for OP-TEE Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-18 20:50 ` [PATCH v6 6/6] optee: implement the RPMB subsystem interface for UFS Jorge Ramirez-Ortiz
2026-10-19 9:37 ` [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Neil Armstrong
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
rpmb.c implements the legacy single-command RPMB supplicant, which is
eMMC-only. Rename it to rpmb_emmc.c to make room for a UFS backend.
No functional change.
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
drivers/tee/optee/Makefile | 2 +-
drivers/tee/optee/{rpmb.c => rpmb_emmc.c} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename drivers/tee/optee/{rpmb.c => rpmb_emmc.c} (100%)
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index 36ac085ef42..d0c7f8dfb12 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -3,4 +3,4 @@
obj-$(CONFIG_OPTEE) += core.o
obj-y += supplicant.o
obj-$(CONFIG_DM_I2C) += i2c.o
-obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb_emmc.o
diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb_emmc.c
similarity index 100%
rename from drivers/tee/optee/rpmb.c
rename to drivers/tee/optee/rpmb_emmc.c
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 6/6] optee: implement the RPMB subsystem interface for UFS
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
` (4 preceding siblings ...)
2026-10-18 20:50 ` [PATCH v6 5/6] optee: rename rpmb.c to rpmb_emmc.c Jorge Ramirez-Ortiz
@ 2026-10-18 20:50 ` Jorge Ramirez-Ortiz
2026-10-19 9:37 ` [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Neil Armstrong
6 siblings, 0 replies; 8+ messages in thread
From: Jorge Ramirez-Ortiz @ 2026-10-18 20:50 UTC (permalink / raw)
To: jorge.ramirez, trini, neil.armstrong, ilias.apalodimas,
jens.wiklander, sumit.garg, dlechner, peng.fan, jh80.chung,
tanmay.kathpalia, bhupesh.linux, n-francis, xypron.glpk,
s-vadapalli, michal.simek, jbx6244, marek.vasut+renesas
Cc: stanleyjhu, Macpaul.Lin, u-boot
The legacy RPMB supplicant assumes an eMMC device and derives the key
from an eMMC-shaped device ID, so it can never reproduce the key Linux
derives for a UFS device. Secure storage on UFS-only platforms instead
needs the transport-agnostic RPMB subsystem interface, through which the
normal world describes the real RPMB device to OP-TEE.
Add a UFS backend for it so OP-TEE derives a key matching Linux and can
use RPMB secure storage on UFS. eMMC keeps using the legacy interface.
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/optee_msg_supplicant.h | 6 +
drivers/tee/optee/optee_private.h | 41 +++++++
drivers/tee/optee/rpmb_ufs.c | 141 +++++++++++++++++++++++
drivers/tee/optee/supplicant.c | 9 ++
drivers/ufs/Kconfig | 8 ++
6 files changed, 206 insertions(+)
create mode 100644 drivers/tee/optee/rpmb_ufs.c
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index d0c7f8dfb12..f2af10999a9 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_OPTEE) += core.o
obj-y += supplicant.o
obj-$(CONFIG_DM_I2C) += i2c.o
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb_emmc.o
+obj-$(CONFIG_SUPPORT_UFS_RPMB) += rpmb_ufs.o
diff --git a/drivers/tee/optee/optee_msg_supplicant.h b/drivers/tee/optee/optee_msg_supplicant.h
index 963cfd47824..a7b569d3e54 100644
--- a/drivers/tee/optee/optee_msg_supplicant.h
+++ b/drivers/tee/optee/optee_msg_supplicant.h
@@ -152,6 +152,12 @@
*/
#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER 21
+#define OPTEE_MSG_RPC_CMD_RPMB_PROBE_RESET 22
+#define OPTEE_MSG_RPC_CMD_RPMB_PROBE_NEXT 23
+#define OPTEE_MSG_RPC_CMD_RPMB_FRAMES 24
+
+#define OPTEE_RPC_RPMB_UFS 1
+
/*
* Was OPTEE_MSG_RPC_CMD_SQL_FS, which isn't supported any longer
*/
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index daff3e04493..5c72b280058 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -30,6 +30,17 @@
*/
#define PTA_CMD_GET_DEVICES_RPMB 0x2
+#ifdef CONFIG_SUPPORT_UFS_RPMB
+/**
+ * struct optee_private - OP-TEE driver private data
+ * @rpmb_next_region: next UFS RPMB region to report on PROBE_NEXT
+ * @rpmb_cur_region: UFS RPMB region selected by the last PROBE_NEXT
+ */
+struct optee_private {
+ unsigned int rpmb_next_region;
+ unsigned int rpmb_cur_region;
+};
+#else
/**
* struct optee_private - OP-TEE driver private data
* @rpmb_mmc: mmc device for the RPMB partition
@@ -43,6 +54,7 @@ struct optee_private {
int rpmb_dev_id;
int rpmb_original_part;
};
+#endif
struct optee_msg_arg;
@@ -84,6 +96,35 @@ static inline void optee_suppl_rpmb_release(struct udevice *dev)
}
#endif
+#ifdef CONFIG_SUPPORT_UFS_RPMB
+void optee_suppl_cmd_rpmb_probe_reset(struct udevice *dev,
+ struct optee_msg_arg *arg);
+
+void optee_suppl_cmd_rpmb_probe_next(struct udevice *dev,
+ struct optee_msg_arg *arg);
+
+void optee_suppl_cmd_rpmb_frames(struct udevice *dev,
+ struct optee_msg_arg *arg);
+#else
+static inline void optee_suppl_cmd_rpmb_probe_reset(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+
+static inline void optee_suppl_cmd_rpmb_probe_next(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+
+static inline void optee_suppl_cmd_rpmb_frames(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
#ifdef CONFIG_DM_I2C
/**
* optee_suppl_cmd_i2c_transfer() - route I2C requests to an I2C chip
diff --git a/drivers/tee/optee/rpmb_ufs.c b/drivers/tee/optee/rpmb_ufs.c
new file mode 100644
index 00000000000..0c820e9595d
--- /dev/null
+++ b/drivers/tee/optee/rpmb_ufs.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <tee.h>
+#include <ufs.h>
+
+#include "optee_msg.h"
+#include "optee_msg_supplicant.h"
+#include "optee_private.h"
+
+static int optee_rpmb_get_dev(struct udevice **scsi_devp)
+{
+ struct udevice *ufs_dev, *scsi_dev;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_UFS, CONFIG_UFS_RPMB_CONTROLLER,
+ &ufs_dev);
+ if (ret)
+ return ret;
+
+ ret = device_get_child(ufs_dev, 0, &scsi_dev);
+ if (ret)
+ return ret;
+
+ *scsi_devp = scsi_dev;
+
+ return 0;
+}
+
+void optee_suppl_cmd_rpmb_probe_reset(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ struct optee_private *priv = dev_get_priv(dev);
+
+ if (arg->num_params != 1 ||
+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT) {
+ arg->ret = TEE_ERROR_BAD_PARAMETERS;
+ return;
+ }
+
+ priv->rpmb_next_region = 0;
+ priv->rpmb_cur_region = 0;
+
+ arg->params[0].u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL;
+ arg->ret = TEE_SUCCESS;
+}
+
+void optee_suppl_cmd_rpmb_probe_next(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ struct optee_private *priv = dev_get_priv(dev);
+ struct udevice *scsi_dev;
+ struct tee_shm *cid_shm;
+ u8 size_mult = 0;
+ u8 rel_wr = 0;
+ void *cid_buf;
+ ulong cid_size;
+ int ret;
+
+ if (arg->num_params != 2 ||
+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT ||
+ arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) {
+ arg->ret = TEE_ERROR_BAD_PARAMETERS;
+ return;
+ }
+
+ cid_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref;
+ cid_buf = (u8 *)cid_shm->addr + arg->params[1].u.rmem.offs;
+ cid_size = arg->params[1].u.rmem.size;
+ if (cid_size < UFS_RPMB_CID_SIZE) {
+ arg->ret = TEE_ERROR_SHORT_BUFFER;
+ return;
+ }
+
+ if (optee_rpmb_get_dev(&scsi_dev)) {
+ arg->ret = TEE_ERROR_ITEM_NOT_FOUND;
+ return;
+ }
+
+ while (priv->rpmb_next_region < UFS_RPMB_NUM_REGIONS) {
+ unsigned int region = priv->rpmb_next_region++;
+
+ ret = ufs_rpmb_get_region_info(scsi_dev, region, &size_mult,
+ &rel_wr, cid_buf);
+ if (ret < 0) {
+ arg->ret = TEE_ERROR_GENERIC;
+ return;
+ }
+ if (!ret)
+ continue;
+
+ priv->rpmb_cur_region = region;
+ arg->params[0].u.value.a = OPTEE_RPC_RPMB_UFS;
+ arg->params[0].u.value.b = size_mult;
+ arg->params[0].u.value.c = rel_wr;
+ arg->params[1].u.rmem.size = UFS_RPMB_CID_SIZE;
+ arg->ret = TEE_SUCCESS;
+ return;
+ }
+
+ arg->ret = TEE_ERROR_ITEM_NOT_FOUND;
+}
+
+void optee_suppl_cmd_rpmb_frames(struct udevice *dev,
+ struct optee_msg_arg *arg)
+{
+ struct optee_private *priv = dev_get_priv(dev);
+ struct tee_shm *req_shm;
+ struct tee_shm *rsp_shm;
+ struct udevice *scsi_dev;
+ void *req_buf;
+ void *rsp_buf;
+ ulong req_size;
+ ulong rsp_size;
+
+ if (arg->num_params != 2 ||
+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT ||
+ arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) {
+ arg->ret = TEE_ERROR_BAD_PARAMETERS;
+ return;
+ }
+
+ if (optee_rpmb_get_dev(&scsi_dev)) {
+ arg->ret = TEE_ERROR_ITEM_NOT_FOUND;
+ return;
+ }
+
+ req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref;
+ req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs;
+ req_size = arg->params[0].u.rmem.size;
+
+ rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref;
+ rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs;
+ rsp_size = arg->params[1].u.rmem.size;
+
+ if (ufs_rpmb_route_frames(scsi_dev, priv->rpmb_cur_region, req_buf,
+ req_size, rsp_buf, rsp_size))
+ arg->ret = TEE_ERROR_BAD_PARAMETERS;
+ else
+ arg->ret = TEE_SUCCESS;
+}
diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c
index 8a426f53ba8..50b780037fb 100644
--- a/drivers/tee/optee/supplicant.c
+++ b/drivers/tee/optee/supplicant.c
@@ -89,6 +89,15 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg,
case OPTEE_MSG_RPC_CMD_RPMB:
optee_suppl_cmd_rpmb(dev, arg);
break;
+ case OPTEE_MSG_RPC_CMD_RPMB_PROBE_RESET:
+ optee_suppl_cmd_rpmb_probe_reset(dev, arg);
+ break;
+ case OPTEE_MSG_RPC_CMD_RPMB_PROBE_NEXT:
+ optee_suppl_cmd_rpmb_probe_next(dev, arg);
+ break;
+ case OPTEE_MSG_RPC_CMD_RPMB_FRAMES:
+ optee_suppl_cmd_rpmb_frames(dev, arg);
+ break;
case OPTEE_MSG_RPC_CMD_I2C_TRANSFER:
optee_suppl_cmd_i2c_transfer(arg);
break;
diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
index 648b58b46e4..f174ca0841d 100644
--- a/drivers/ufs/Kconfig
+++ b/drivers/ufs/Kconfig
@@ -105,4 +105,12 @@ config SUPPORT_UFS_RPMB
single RPMB transport, so this is mutually exclusive with the eMMC
RPMB supplicant (SUPPORT_EMMC_RPMB).
+config UFS_RPMB_CONTROLLER
+ int "UFS controller index used for RPMB"
+ depends on SUPPORT_UFS_RPMB
+ default 0
+ help
+ Index of the RPMB-owning UFS controller in the UCLASS_UFS device
+ list. Leave at 0 unless the board has more than one UFS controller.
+
endmenu
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS
2026-10-18 20:50 [PATCH v6 0/6] ufs: rpmb: route OP-TEE RPMB secure storage over UFS Jorge Ramirez-Ortiz
` (5 preceding siblings ...)
2026-10-18 20:50 ` [PATCH v6 6/6] optee: implement the RPMB subsystem interface for UFS Jorge Ramirez-Ortiz
@ 2026-10-19 9:37 ` Neil Armstrong
6 siblings, 0 replies; 8+ messages in thread
From: Neil Armstrong @ 2026-10-19 9:37 UTC (permalink / raw)
To: trini, ilias.apalodimas, sumit.garg, dlechner, peng.fan,
jh80.chung, tanmay.kathpalia, bhupesh.linux, n-francis,
xypron.glpk, s-vadapalli, michal.simek, jbx6244,
marek.vasut+renesas, Jens Wiklander, Jorge Ramirez-Ortiz
Cc: stanleyjhu, Macpaul.Lin, u-boot
Hi,
On Sun, 18 Oct 2026 22:50:50 +0200, Jorge Ramirez-Ortiz wrote:
> OP-TEE secure storage (CFG_RPMB_FS) relies on an RPMB partition, but
> U-Boot's OP-TEE RPMB supplicant only speaks the legacy single-command
> interface, which is bound to eMMC. SoCs that are UFS-only and have no
> eMMC (for example the Qualcomm SA8775P) therefore cannot back OP-TEE
> secure storage from U-Boot today. This series adds that support.
>
> It introduces the transport-agnostic OP-TEE RPMB "subsystem" interface
> (PROBE_RESET / PROBE_NEXT / FRAMES), where the normal world enumerates
> the RPMB device and reports its kind, size and CID, then carries the
> signed frames. The legacy eMMC supplicant is preserved unchanged, only
> renamed to rpmb_emmc.c, with the new UFS backend added as a separate
> rpmb_ufs.c; the two are mutually exclusive via Kconfig (SUPPORT_UFS_RPMB
> depends on !SUPPORT_EMMC_RPMB) because the OP-TEE supplicant handles a
> single RPMB transport. The subsystem interface is UFS-only for now; eMMC
> can be migrated onto it later as the legacy path is retired.
>
> [...]
Thanks, Applied to https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs (u-boot-ufs-next)
[1/6] ufs: decode string descriptors as UTF-16 big-endian
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/01c4857ccc5d2a04e8c522335308b08c9027f4b3
[2/6] ufs: surface sense data from ufs_scsi_exec()
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/768aa39691e031acfa53cd7b8bf119e4527cdfb8
[3/6] ufs: add RPMB transport over SCSI SECURITY PROTOCOL
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/b3cd7ddbbdd57fc4788b7117bf60523cddd90bd8
[4/6] ufs: derive the per-region RPMB CID and size for OP-TEE
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/87094d8062bdda5b5e45a35d9320b40647e12593
[5/6] optee: rename rpmb.c to rpmb_emmc.c
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/20334a6bfdc4dcc061925197cb1ccff4d95ddb2c
[6/6] optee: implement the RPMB subsystem interface for UFS
https://git.u-boot-project.org/u-boot/custodians/neil.armstrong/u-boot-ufs/-/commit/3b39df3bc540dbc8d60d54570673b25d6698153a
--
Neil
^ permalink raw reply [flat|nested] 8+ messages in thread