Skip to content

Commit 1e9046e

Browse files
jenswi-linarostorulf
authored andcommitted
rpmb: add Replay Protected Memory Block (RPMB) subsystem
A number of storage technologies support a specialised hardware partition designed to be resistant to replay attacks. The underlying HW protocols differ but the operations are common. The RPMB partition cannot be accessed via standard block layer, but by a set of specific RPMB commands. Such a partition provides authenticated and replay protected access, hence suitable as a secure storage. The initial aim of this patch is to provide a simple RPMB driver interface which can be accessed by the optee driver to facilitate early RPMB access to OP-TEE OS (secure OS) during the boot time. A TEE device driver can claim the RPMB interface, for example, via rpmb_interface_register() or rpmb_dev_find_device(). The RPMB driver provides a callback to route RPMB frames to the RPMB device accessible via rpmb_route_frames(). The detailed operation of implementing the access is left to the TEE device driver itself. Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Shyam Saini <shyamsaini@linux.microsoft.com> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Tested-by: Manuel Traut <manut@mecka.net> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20240814153558.708365-2-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent 0579ac4 commit 1e9046e

File tree

5 files changed

+374
-0
lines changed

5 files changed

+374
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19861,6 +19861,13 @@ T: git git://linuxtv.org/media_tree.git
1986119861
F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
1986219862
F: drivers/media/platform/sunxi/sun8i-rotate/
1986319863

19864+
RPMB SUBSYSTEM
19865+
M: Jens Wiklander <jens.wiklander@linaro.org>
19866+
L: linux-kernel@vger.kernel.org
19867+
S: Supported
19868+
F: drivers/misc/rpmb-core.c
19869+
F: include/linux/rpmb.h
19870+
1986419871
RPMSG TTY DRIVER
1986519872
M: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
1986619873
L: linux-remoteproc@vger.kernel.org

drivers/misc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ config PHANTOM
104104
If you choose to build module, its name will be phantom. If unsure,
105105
say N here.
106106

107+
config RPMB
108+
tristate "RPMB partition interface"
109+
depends on MMC
110+
help
111+
Unified RPMB unit interface for RPMB capable devices such as eMMC and
112+
UFS. Provides interface for in-kernel security controllers to access
113+
RPMB unit.
114+
115+
If unsure, select N.
116+
107117
config TIFM_CORE
108118
tristate "TI Flash Media interface support"
109119
depends on PCI

drivers/misc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/
1515
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
1616
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
1717
obj-$(CONFIG_PHANTOM) += phantom.o
18+
obj-$(CONFIG_RPMB) += rpmb-core.o
1819
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
1920
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
2021
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o

drivers/misc/rpmb-core.c

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
4+
* Copyright(c) 2021 - 2024 Linaro Ltd.
5+
*/
6+
#include <linux/device.h>
7+
#include <linux/init.h>
8+
#include <linux/kernel.h>
9+
#include <linux/list.h>
10+
#include <linux/module.h>
11+
#include <linux/mutex.h>
12+
#include <linux/rpmb.h>
13+
#include <linux/slab.h>
14+
15+
static DEFINE_IDA(rpmb_ida);
16+
static DEFINE_MUTEX(rpmb_mutex);
17+
18+
/**
19+
* rpmb_dev_get() - increase rpmb device ref counter
20+
* @rdev: rpmb device
21+
*/
22+
struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
23+
{
24+
if (rdev)
25+
get_device(&rdev->dev);
26+
return rdev;
27+
}
28+
EXPORT_SYMBOL_GPL(rpmb_dev_get);
29+
30+
/**
31+
* rpmb_dev_put() - decrease rpmb device ref counter
32+
* @rdev: rpmb device
33+
*/
34+
void rpmb_dev_put(struct rpmb_dev *rdev)
35+
{
36+
if (rdev)
37+
put_device(&rdev->dev);
38+
}
39+
EXPORT_SYMBOL_GPL(rpmb_dev_put);
40+
41+
/**
42+
* rpmb_route_frames() - route rpmb frames to rpmb device
43+
* @rdev: rpmb device
44+
* @req: rpmb request frames
45+
* @req_len: length of rpmb request frames in bytes
46+
* @rsp: rpmb response frames
47+
* @rsp_len: length of rpmb response frames in bytes
48+
*
49+
* Returns: < 0 on failure
50+
*/
51+
int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
52+
unsigned int req_len, u8 *rsp, unsigned int rsp_len)
53+
{
54+
if (!req || !req_len || !rsp || !rsp_len)
55+
return -EINVAL;
56+
57+
return rdev->descr.route_frames(rdev->dev.parent, req, req_len,
58+
rsp, rsp_len);
59+
}
60+
EXPORT_SYMBOL_GPL(rpmb_route_frames);
61+
62+
static void rpmb_dev_release(struct device *dev)
63+
{
64+
struct rpmb_dev *rdev = to_rpmb_dev(dev);
65+
66+
mutex_lock(&rpmb_mutex);
67+
ida_simple_remove(&rpmb_ida, rdev->id);
68+
mutex_unlock(&rpmb_mutex);
69+
kfree(rdev->descr.dev_id);
70+
kfree(rdev);
71+
}
72+
73+
static struct class rpmb_class = {
74+
.name = "rpmb",
75+
.dev_release = rpmb_dev_release,
76+
};
77+
78+
/**
79+
* rpmb_dev_find_device() - return first matching rpmb device
80+
* @start: rpmb device to begin with
81+
* @data: data for the match function
82+
* @match: the matching function
83+
*
84+
* Iterate over registered RPMB devices, and call @match() for each passing
85+
* it the RPMB device and @data.
86+
*
87+
* The return value of @match() is checked for each call. If it returns
88+
* anything other 0, break and return the found RPMB device.
89+
*
90+
* It's the callers responsibility to call rpmb_dev_put() on the returned
91+
* device, when it's done with it.
92+
*
93+
* Returns: a matching rpmb device or NULL on failure
94+
*/
95+
struct rpmb_dev *rpmb_dev_find_device(const void *data,
96+
const struct rpmb_dev *start,
97+
int (*match)(struct device *dev,
98+
const void *data))
99+
{
100+
struct device *dev;
101+
const struct device *start_dev = NULL;
102+
103+
if (start)
104+
start_dev = &start->dev;
105+
dev = class_find_device(&rpmb_class, start_dev, data, match);
106+
107+
return dev ? to_rpmb_dev(dev) : NULL;
108+
}
109+
EXPORT_SYMBOL_GPL(rpmb_dev_find_device);
110+
111+
int rpmb_interface_register(struct class_interface *intf)
112+
{
113+
intf->class = &rpmb_class;
114+
115+
return class_interface_register(intf);
116+
}
117+
EXPORT_SYMBOL_GPL(rpmb_interface_register);
118+
119+
void rpmb_interface_unregister(struct class_interface *intf)
120+
{
121+
class_interface_unregister(intf);
122+
}
123+
EXPORT_SYMBOL_GPL(rpmb_interface_unregister);
124+
125+
/**
126+
* rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
127+
* @rdev: the rpmb device to unregister
128+
*
129+
* This function should be called from the release function of the
130+
* underlying device used when the RPMB device was registered.
131+
*
132+
* Returns: < 0 on failure
133+
*/
134+
int rpmb_dev_unregister(struct rpmb_dev *rdev)
135+
{
136+
if (!rdev)
137+
return -EINVAL;
138+
139+
device_del(&rdev->dev);
140+
141+
rpmb_dev_put(rdev);
142+
143+
return 0;
144+
}
145+
EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
146+
147+
/**
148+
* rpmb_dev_register - register RPMB partition with the RPMB subsystem
149+
* @dev: storage device of the rpmb device
150+
* @descr: RPMB device description
151+
*
152+
* While registering the RPMB partition extract needed device information
153+
* while needed resources are available.
154+
*
155+
* Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure
156+
*/
157+
struct rpmb_dev *rpmb_dev_register(struct device *dev,
158+
struct rpmb_descr *descr)
159+
{
160+
struct rpmb_dev *rdev;
161+
int ret;
162+
163+
if (!dev || !descr || !descr->route_frames || !descr->dev_id ||
164+
!descr->dev_id_len)
165+
return ERR_PTR(-EINVAL);
166+
167+
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
168+
if (!rdev)
169+
return ERR_PTR(-ENOMEM);
170+
rdev->descr = *descr;
171+
rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len,
172+
GFP_KERNEL);
173+
if (!rdev->descr.dev_id) {
174+
ret = -ENOMEM;
175+
goto err_free_rdev;
176+
}
177+
178+
mutex_lock(&rpmb_mutex);
179+
ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
180+
mutex_unlock(&rpmb_mutex);
181+
if (ret < 0)
182+
goto err_free_dev_id;
183+
rdev->id = ret;
184+
185+
dev_set_name(&rdev->dev, "rpmb%d", rdev->id);
186+
rdev->dev.class = &rpmb_class;
187+
rdev->dev.parent = dev;
188+
189+
ret = device_register(&rdev->dev);
190+
if (ret)
191+
goto err_id_remove;
192+
193+
dev_dbg(&rdev->dev, "registered device\n");
194+
195+
return rdev;
196+
197+
err_id_remove:
198+
mutex_lock(&rpmb_mutex);
199+
ida_simple_remove(&rpmb_ida, rdev->id);
200+
mutex_unlock(&rpmb_mutex);
201+
err_free_dev_id:
202+
kfree(rdev->descr.dev_id);
203+
err_free_rdev:
204+
kfree(rdev);
205+
return ERR_PTR(ret);
206+
}
207+
EXPORT_SYMBOL_GPL(rpmb_dev_register);
208+
209+
static int __init rpmb_init(void)
210+
{
211+
int ret;
212+
213+
ret = class_register(&rpmb_class);
214+
if (ret) {
215+
pr_err("couldn't create class\n");
216+
return ret;
217+
}
218+
ida_init(&rpmb_ida);
219+
return 0;
220+
}
221+
222+
static void __exit rpmb_exit(void)
223+
{
224+
ida_destroy(&rpmb_ida);
225+
class_unregister(&rpmb_class);
226+
}
227+
228+
subsys_initcall(rpmb_init);
229+
module_exit(rpmb_exit);
230+
231+
MODULE_AUTHOR("Jens Wiklander <jens.wiklander@linaro.org>");
232+
MODULE_DESCRIPTION("RPMB class");
233+
MODULE_LICENSE("GPL");

include/linux/rpmb.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2015-2019 Intel Corp. All rights reserved
4+
* Copyright (C) 2021-2022 Linaro Ltd
5+
*/
6+
#ifndef __RPMB_H__
7+
#define __RPMB_H__
8+
9+
#include <linux/device.h>
10+
#include <linux/types.h>
11+
12+
/**
13+
* enum rpmb_type - type of underlying storage technology
14+
*
15+
* @RPMB_TYPE_EMMC : emmc (JESD84-B50.1)
16+
* @RPMB_TYPE_UFS : UFS (JESD220)
17+
* @RPMB_TYPE_NVME : NVM Express
18+
*/
19+
enum rpmb_type {
20+
RPMB_TYPE_EMMC,
21+
RPMB_TYPE_UFS,
22+
RPMB_TYPE_NVME,
23+
};
24+
25+
/**
26+
* struct rpmb_descr - RPMB description provided by the underlying block device
27+
*
28+
* @type : block device type
29+
* @route_frames : routes frames to and from the RPMB device
30+
* @dev_id : unique device identifier read from the hardware
31+
* @dev_id_len : length of unique device identifier
32+
* @reliable_wr_count: number of sectors that can be written in one access
33+
* @capacity : capacity of the device in units of 128K
34+
*
35+
* @dev_id is intended to be used as input when deriving the authenticaion key.
36+
*/
37+
struct rpmb_descr {
38+
enum rpmb_type type;
39+
int (*route_frames)(struct device *dev, u8 *req, unsigned int req_len,
40+
u8 *resp, unsigned int resp_len);
41+
u8 *dev_id;
42+
size_t dev_id_len;
43+
u16 reliable_wr_count;
44+
u16 capacity;
45+
};
46+
47+
/**
48+
* struct rpmb_dev - device which can support RPMB partition
49+
*
50+
* @dev : device
51+
* @id : device_id
52+
* @list_node : linked list node
53+
* @descr : RPMB description
54+
*/
55+
struct rpmb_dev {
56+
struct device dev;
57+
int id;
58+
struct list_head list_node;
59+
struct rpmb_descr descr;
60+
};
61+
62+
#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
63+
64+
#if IS_ENABLED(CONFIG_RPMB)
65+
struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
66+
void rpmb_dev_put(struct rpmb_dev *rdev);
67+
struct rpmb_dev *rpmb_dev_find_device(const void *data,
68+
const struct rpmb_dev *start,
69+
int (*match)(struct device *dev,
70+
const void *data));
71+
int rpmb_interface_register(struct class_interface *intf);
72+
void rpmb_interface_unregister(struct class_interface *intf);
73+
struct rpmb_dev *rpmb_dev_register(struct device *dev,
74+
struct rpmb_descr *descr);
75+
int rpmb_dev_unregister(struct rpmb_dev *rdev);
76+
77+
int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
78+
unsigned int req_len, u8 *resp, unsigned int resp_len);
79+
80+
#else
81+
static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
82+
{
83+
return NULL;
84+
}
85+
86+
static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
87+
88+
static inline struct rpmb_dev *
89+
rpmb_dev_find_device(const void *data, const struct rpmb_dev *start,
90+
int (*match)(struct device *dev, const void *data))
91+
{
92+
return NULL;
93+
}
94+
95+
static inline int rpmb_interface_register(struct class_interface *intf)
96+
{
97+
return -EOPNOTSUPP;
98+
}
99+
100+
static inline void rpmb_interface_unregister(struct class_interface *intf)
101+
{
102+
}
103+
104+
static inline struct rpmb_dev *
105+
rpmb_dev_register(struct device *dev, struct rpmb_descr *descr)
106+
{
107+
return NULL;
108+
}
109+
110+
static inline int rpmb_dev_unregister(struct rpmb_dev *dev)
111+
{
112+
return 0;
113+
}
114+
115+
static inline int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
116+
unsigned int req_len, u8 *resp,
117+
unsigned int resp_len)
118+
{
119+
return -EOPNOTSUPP;
120+
}
121+
#endif /* CONFIG_RPMB */
122+
123+
#endif /* __RPMB_H__ */

0 commit comments

Comments
 (0)