Merge patch series "ufs: rpmb: make RPMB usable with OP-TEE key derivation"

Jorge Ramirez-Ortiz <jorge.ramirez@oss.qualcomm.com> says:

This series makes UFS RPMB work out of the box with an OP-TEE that
implements the standard eMMC RPMB key-derivation flow, without requiring
any fundamental changes on the OP-TEE side.

RPMB provides an authenticated, replay-protected storage area whose
security relies on a secret authentication key. In our setup that key is
never exposed to the kernel: OP-TEE derives it in the secure world from its
hardware-unique key and a device identifier (dev_id) that the RPMB core
hands down. OP-TEE's implementation targets eMMC, where dev_id is the
16-byte eMMC CID, and both the fixed length and the raw-CID layout are
baked into its key derivation.

Two things stand in the way of reusing that same, unmodified OP-TEE flow
for UFS RPMB:

  1. On a cold boot the very first frame sent to the RPMB well-known LU
     comes back with a power-on UNIT ATTENTION (ASC 0x29), which the SCSI
     core reports rather than retries. RPMB has no earlier guaranteed
     access that could clear the condition first, so RPMB fails on every
     power cycle. Patch 1 asks the SCSI core to retry the power-on UNIT
     ATTENTION on the RPMB WLUN.

  2. The UFS RPMB id is "<device_id>-R<region>", which is variable length
     and longer than 16 bytes. Passing it verbatim would tie the derived
     key to a length OP-TEE does not expect and diverge from the fixed eMMC
     CID ABI. Patch 2 hashes it into a fixed 16-byte dev_id with blake2b,
     keeping the key stable and unique per region while matching the eMMC
     CID layout OP-TEE relies on. The hash algorithm and input string are
     thus part of the key-derivation ABI and must stay stable.

With both patches, UFS RPMB is functional from the first access after a
cold boot and derives keys through the existing eMMC-style OP-TEE flow,
(requires minimal OP-TEE changes pending on the CID proposal done here).

Tested on IQ-9075 with Open Firmware [1], pending OP-TEE changes
[1]https://ldts.github.io/qcom-buildroot

Dependencies:

U-boot:
https://lore.kernel.org/u-boot/20260720085202.537019-1-jorge.ramirez@oss.qualcomm.com/T/#mc423eb4dcf8a15849077029e4f7c1913bb7d8873

OP-TEE:
https://github.com/OP-TEE/optee_os/pull/7881

Link: https://patch.msgid.link/20260831154804.719528-1-jorge.ramirez@oss.qualcomm.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Martin K. Petersen (Oracle) 2026-09-09 21:54:19 -04:00
commit 06cc447b65
2 changed files with 27 additions and 3 deletions

View File

@ -7,6 +7,7 @@ menuconfig SCSI_UFSHCD
tristate "Universal Flash Storage Controller"
depends on SCSI && SCSI_DMA
depends on RPMB || !RPMB
select CRYPTO_LIB_BLAKE2B if RPMB
select PM_DEVFREQ
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select NLS

View File

@ -10,6 +10,7 @@
* Can Guo <can.guo@oss.qualcomm.com>
*/
#include <crypto/blake2b.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
@ -21,6 +22,8 @@
#include <linux/unaligned.h>
#include "ufshcd-priv.h"
#define UFS_RPMB_ID_LEN 16 /* Match eMMC CID Length */
#define UFS_RPMB_UA_RETRIES 3 /* Retries for the power-on UNIT ATTENTION */
#define UFS_RPMB_SEC_PROTOCOL 0xEC /* JEDEC UFS application */
#define UFS_RPMB_SEC_PROTOCOL_ID 0x01 /* JEDEC UFS RPMB protocol ID, CDB byte3 */
@ -40,6 +43,22 @@ struct ufs_rpmb_dev {
static int ufs_sec_submit(struct ufs_hba *hba, u16 spsp, void *buffer, size_t len, bool send)
{
struct scsi_device *sdev = hba->ufs_rpmb_wlun;
struct scsi_failure failure_defs[] = {
{
.sense = UNIT_ATTENTION,
.asc = 0x29, /* power on, reset, or bus device reset occurred */
.ascq = SCMD_FAILURE_ASCQ_ANY,
.allowed = UFS_RPMB_UA_RETRIES,
.result = SAM_STAT_CHECK_CONDITION,
},
{}
};
struct scsi_failures failures = {
.failure_definitions = failure_defs,
};
const struct scsi_exec_args exec_args = {
.failures = &failures,
};
u8 cdb[12] = { };
cdb[0] = send ? SECURITY_PROTOCOL_OUT : SECURITY_PROTOCOL_IN;
@ -48,7 +67,8 @@ static int ufs_sec_submit(struct ufs_hba *hba, u16 spsp, void *buffer, size_t le
put_unaligned_be32(len, &cdb[6]);
return scsi_execute_cmd(sdev, cdb, send ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
buffer, len, /*timeout=*/30 * HZ, 0, NULL);
buffer, len, /*timeout=*/30 * HZ, /*retries=*/0,
&exec_args);
}
/* UFS RPMB route frames implementation */
@ -139,6 +159,7 @@ static void ufs_rpmb_device_release(struct device *dev)
int ufs_rpmb_probe(struct ufs_hba *hba)
{
struct ufs_rpmb_dev *ufs_rpmb, *it, *tmp;
u8 dev_id[UFS_RPMB_ID_LEN];
struct rpmb_dev *rdev;
char *cid = NULL;
int region;
@ -197,8 +218,10 @@ int ufs_rpmb_probe(struct ufs_hba *hba)
goto err_out;
}
descr.dev_id = cid;
descr.dev_id_len = strlen(cid);
blake2b(NULL, 0, cid, strlen(cid), dev_id, UFS_RPMB_ID_LEN);
descr.dev_id = dev_id;
descr.dev_id_len = UFS_RPMB_ID_LEN;
descr.capacity = cap;
/* Register RPMB device */