Skip to content

Commit b06b8c4

Browse files
beanhuomartinkpetersen
authored andcommitted
scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices
Add OP-TEE based RPMB support for UFS devices. This enables secure RPMB operations on UFS devices through OP-TEE, providing the same functionality available for eMMC devices and extending kernel-based secure storage support to UFS-based systems. Benefits of OP-TEE based RPMB implementation: - Eliminates dependency on userspace supplicant for RPMB access - Enables early boot secure storage access (e.g., fTPM, secure UEFI variables) - Provides kernel-level RPMB access as soon as UFS driver is initialized - Removes complex initramfs dependencies and boot ordering requirements - Ensures reliable and deterministic secure storage operations - Supports both built-in and modular fTPM configurations [mkp: make this build as a module] Co-developed-by: Can Guo <can.guo@oss.qualcomm.com> Signed-off-by: Can Guo <can.guo@oss.qualcomm.com> Reviewed-by: Avri Altman <avri.altman@sandisk.com> Reviewed-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Bean Huo <beanhuo@micron.com> Link: https://patch.msgid.link/20251107230518.4060231-4-beanhuo@iokpp.de Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent d794b49 commit b06b8c4

7 files changed

Lines changed: 361 additions & 7 deletions

File tree

drivers/misc/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ config PHANTOM
106106

107107
config RPMB
108108
tristate "RPMB partition interface"
109-
depends on MMC
109+
depends on MMC || SCSI_UFSHCD
110110
help
111111
Unified RPMB unit interface for RPMB capable devices such as eMMC and
112112
UFS. Provides interface for in-kernel security controllers to access

drivers/ufs/core/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
44
ufshcd-core-y += ufshcd.o ufs-sysfs.o ufs-mcq.o
5+
ufshcd-core-$(CONFIG_RPMB) += ufs-rpmb.o
56
ufshcd-core-$(CONFIG_DEBUG_FS) += ufs-debugfs.o
67
ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o
78
ufshcd-core-$(CONFIG_SCSI_UFS_CRYPTO) += ufshcd-crypto.o

drivers/ufs/core/ufs-rpmb.c

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* UFS OP-TEE based RPMB Driver
4+
*
5+
* Copyright (C) 2025 Micron Technology, Inc.
6+
* Copyright (C) 2025 Qualcomm Technologies, Inc.
7+
*
8+
* Authors:
9+
* Bean Huo <beanhuo@micron.com>
10+
* Can Guo <can.guo@oss.qualcomm.com>
11+
*/
12+
13+
#include <linux/module.h>
14+
#include <linux/device.h>
15+
#include <linux/kernel.h>
16+
#include <linux/types.h>
17+
#include <linux/rpmb.h>
18+
#include <linux/string.h>
19+
#include <linux/list.h>
20+
#include <ufs/ufshcd.h>
21+
#include <linux/unaligned.h>
22+
#include "ufshcd-priv.h"
23+
24+
#define UFS_RPMB_SEC_PROTOCOL 0xEC /* JEDEC UFS application */
25+
#define UFS_RPMB_SEC_PROTOCOL_ID 0x01 /* JEDEC UFS RPMB protocol ID, CDB byte3 */
26+
27+
static const struct bus_type ufs_rpmb_bus_type = {
28+
.name = "ufs_rpmb",
29+
};
30+
31+
/* UFS RPMB device structure */
32+
struct ufs_rpmb_dev {
33+
u8 region_id;
34+
struct device dev;
35+
struct rpmb_dev *rdev;
36+
struct ufs_hba *hba;
37+
struct list_head node;
38+
};
39+
40+
static int ufs_sec_submit(struct ufs_hba *hba, u16 spsp, void *buffer, size_t len, bool send)
41+
{
42+
struct scsi_device *sdev = hba->ufs_rpmb_wlun;
43+
u8 cdb[12] = { };
44+
45+
cdb[0] = send ? SECURITY_PROTOCOL_OUT : SECURITY_PROTOCOL_IN;
46+
cdb[1] = UFS_RPMB_SEC_PROTOCOL;
47+
put_unaligned_be16(spsp, &cdb[2]);
48+
put_unaligned_be32(len, &cdb[6]);
49+
50+
return scsi_execute_cmd(sdev, cdb, send ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
51+
buffer, len, /*timeout=*/30 * HZ, 0, NULL);
52+
}
53+
54+
/* UFS RPMB route frames implementation */
55+
static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_len, u8 *resp,
56+
unsigned int resp_len)
57+
{
58+
struct ufs_rpmb_dev *ufs_rpmb = dev_get_drvdata(dev);
59+
struct rpmb_frame *frm_out = (struct rpmb_frame *)req;
60+
bool need_result_read = true;
61+
u16 req_type, protocol_id;
62+
struct ufs_hba *hba;
63+
int ret;
64+
65+
if (!ufs_rpmb) {
66+
dev_err(dev, "Missing driver data\n");
67+
return -ENODEV;
68+
}
69+
70+
hba = ufs_rpmb->hba;
71+
72+
req_type = be16_to_cpu(frm_out->req_resp);
73+
74+
switch (req_type) {
75+
case RPMB_PROGRAM_KEY:
76+
if (req_len != sizeof(struct rpmb_frame) || resp_len != sizeof(struct rpmb_frame))
77+
return -EINVAL;
78+
break;
79+
case RPMB_GET_WRITE_COUNTER:
80+
if (req_len != sizeof(struct rpmb_frame) || resp_len != sizeof(struct rpmb_frame))
81+
return -EINVAL;
82+
need_result_read = false;
83+
break;
84+
case RPMB_WRITE_DATA:
85+
if (req_len % sizeof(struct rpmb_frame) || resp_len != sizeof(struct rpmb_frame))
86+
return -EINVAL;
87+
break;
88+
case RPMB_READ_DATA:
89+
if (req_len != sizeof(struct rpmb_frame) || resp_len % sizeof(struct rpmb_frame))
90+
return -EINVAL;
91+
need_result_read = false;
92+
break;
93+
default:
94+
dev_err(dev, "Unknown request type=0x%04x\n", req_type);
95+
return -EINVAL;
96+
}
97+
98+
protocol_id = ufs_rpmb->region_id << 8 | UFS_RPMB_SEC_PROTOCOL_ID;
99+
100+
ret = ufs_sec_submit(hba, protocol_id, req, req_len, true);
101+
if (ret) {
102+
dev_err(dev, "Command failed with ret=%d\n", ret);
103+
return ret;
104+
}
105+
106+
if (need_result_read) {
107+
struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp;
108+
109+
memset(frm_resp, 0, sizeof(*frm_resp));
110+
frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ);
111+
ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true);
112+
if (ret) {
113+
dev_err(dev, "Result read request failed with ret=%d\n", ret);
114+
return ret;
115+
}
116+
}
117+
118+
if (!ret) {
119+
ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, false);
120+
if (ret)
121+
dev_err(dev, "Response read failed with ret=%d\n", ret);
122+
}
123+
124+
return ret;
125+
}
126+
127+
static void ufs_rpmb_device_release(struct device *dev)
128+
{
129+
struct ufs_rpmb_dev *ufs_rpmb = dev_get_drvdata(dev);
130+
131+
rpmb_dev_unregister(ufs_rpmb->rdev);
132+
}
133+
134+
/* UFS RPMB device registration */
135+
int ufs_rpmb_probe(struct ufs_hba *hba)
136+
{
137+
struct ufs_rpmb_dev *ufs_rpmb, *it, *tmp;
138+
struct rpmb_dev *rdev;
139+
char *cid = NULL;
140+
int region;
141+
u32 cap;
142+
int ret;
143+
144+
if (!hba->ufs_rpmb_wlun || hba->dev_info.b_advanced_rpmb_en) {
145+
dev_info(hba->dev, "Skip OP-TEE RPMB registration\n");
146+
return -ENODEV;
147+
}
148+
149+
/* Check if device_id is available */
150+
if (!hba->dev_info.device_id) {
151+
dev_err(hba->dev, "UFS Device ID not available\n");
152+
return -EINVAL;
153+
}
154+
155+
INIT_LIST_HEAD(&hba->rpmbs);
156+
157+
struct rpmb_descr descr = {
158+
.type = RPMB_TYPE_UFS,
159+
.route_frames = ufs_rpmb_route_frames,
160+
.reliable_wr_count = hba->dev_info.rpmb_io_size,
161+
};
162+
163+
for (region = 0; region < ARRAY_SIZE(hba->dev_info.rpmb_region_size); region++) {
164+
cap = hba->dev_info.rpmb_region_size[region];
165+
if (!cap)
166+
continue;
167+
168+
ufs_rpmb = devm_kzalloc(hba->dev, sizeof(*ufs_rpmb), GFP_KERNEL);
169+
if (!ufs_rpmb) {
170+
ret = -ENOMEM;
171+
goto err_out;
172+
}
173+
174+
ufs_rpmb->hba = hba;
175+
ufs_rpmb->dev.parent = &hba->ufs_rpmb_wlun->sdev_gendev;
176+
ufs_rpmb->dev.bus = &ufs_rpmb_bus_type;
177+
ufs_rpmb->dev.release = ufs_rpmb_device_release;
178+
dev_set_name(&ufs_rpmb->dev, "ufs_rpmb%d", region);
179+
180+
/* Set driver data BEFORE device_register */
181+
dev_set_drvdata(&ufs_rpmb->dev, ufs_rpmb);
182+
183+
ret = device_register(&ufs_rpmb->dev);
184+
if (ret) {
185+
dev_err(hba->dev, "Failed to register UFS RPMB device %d\n", region);
186+
put_device(&ufs_rpmb->dev);
187+
goto err_out;
188+
}
189+
190+
/* Create unique ID by appending region number to device_id */
191+
cid = kasprintf(GFP_KERNEL, "%s-R%d", hba->dev_info.device_id, region);
192+
if (!cid) {
193+
device_unregister(&ufs_rpmb->dev);
194+
ret = -ENOMEM;
195+
goto err_out;
196+
}
197+
198+
descr.dev_id = cid;
199+
descr.dev_id_len = strlen(cid);
200+
descr.capacity = cap;
201+
202+
/* Register RPMB device */
203+
rdev = rpmb_dev_register(&ufs_rpmb->dev, &descr);
204+
if (IS_ERR(rdev)) {
205+
dev_err(hba->dev, "Failed to register UFS RPMB device.\n");
206+
device_unregister(&ufs_rpmb->dev);
207+
ret = PTR_ERR(rdev);
208+
goto err_out;
209+
}
210+
211+
kfree(cid);
212+
cid = NULL;
213+
214+
ufs_rpmb->rdev = rdev;
215+
ufs_rpmb->region_id = region;
216+
217+
list_add_tail(&ufs_rpmb->node, &hba->rpmbs);
218+
219+
dev_info(hba->dev, "UFS RPMB region %d registered (capacity=%u)\n", region, cap);
220+
}
221+
222+
return 0;
223+
err_out:
224+
kfree(cid);
225+
list_for_each_entry_safe(it, tmp, &hba->rpmbs, node) {
226+
list_del(&it->node);
227+
device_unregister(&it->dev);
228+
}
229+
230+
return ret;
231+
}
232+
233+
/* UFS RPMB remove handler */
234+
void ufs_rpmb_remove(struct ufs_hba *hba)
235+
{
236+
struct ufs_rpmb_dev *ufs_rpmb, *tmp;
237+
238+
if (list_empty(&hba->rpmbs))
239+
return;
240+
241+
/* Remove all registered RPMB devices */
242+
list_for_each_entry_safe(ufs_rpmb, tmp, &hba->rpmbs, node) {
243+
dev_info(hba->dev, "Removing UFS RPMB region %d\n", ufs_rpmb->region_id);
244+
/* Remove from list first */
245+
list_del(&ufs_rpmb->node);
246+
/* Unregister device */
247+
device_unregister(&ufs_rpmb->dev);
248+
}
249+
250+
dev_info(hba->dev, "All UFS RPMB devices unregistered\n");
251+
}
252+
253+
MODULE_LICENSE("GPL v2");
254+
MODULE_DESCRIPTION("OP-TEE UFS RPMB driver");

drivers/ufs/core/ufshcd-priv.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,4 +417,17 @@ static inline u32 ufshcd_mcq_get_sq_head_slot(struct ufs_hw_queue *q)
417417
return val / sizeof(struct utp_transfer_req_desc);
418418
}
419419

420+
#if IS_ENABLED(CONFIG_RPMB)
421+
int ufs_rpmb_probe(struct ufs_hba *hba);
422+
void ufs_rpmb_remove(struct ufs_hba *hba);
423+
#else
424+
static inline int ufs_rpmb_probe(struct ufs_hba *hba)
425+
{
426+
return 0;
427+
}
428+
static inline void ufs_rpmb_remove(struct ufs_hba *hba)
429+
{
430+
}
431+
#endif
432+
420433
#endif /* _UFSHCD_PRIV_H_ */

0 commit comments

Comments
 (0)