On Mon, Apr 22, 2024 at 11:19:34AM +0200, Jens Wiklander wrote: > 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@xxxxxxxxx> > Signed-off-by: Alex Bennée <alex.bennee@xxxxxxxxxx> > Signed-off-by: Shyam Saini <shyamsaini@xxxxxxxxxxxxxxxxxxx> > Signed-off-by: Jens Wiklander <jens.wiklander@xxxxxxxxxx> > Reviewed-by: Linus Walleij <linus.walleij@xxxxxxxxxx> > --- > MAINTAINERS | 7 ++ > drivers/misc/Kconfig | 10 ++ > drivers/misc/Makefile | 1 + > drivers/misc/rpmb-core.c | 232 +++++++++++++++++++++++++++++++++++++++ > include/linux/rpmb.h | 136 +++++++++++++++++++++++ > 5 files changed, 386 insertions(+) > create mode 100644 drivers/misc/rpmb-core.c > create mode 100644 include/linux/rpmb.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 8999497011a2..e83152c42499 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -19012,6 +19012,13 @@ T: git git://linuxtv.org/media_tree.git > F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml > F: drivers/media/platform/sunxi/sun8i-rotate/ > > +RPMB SUBSYSTEM > +M: Jens Wiklander <jens.wiklander@xxxxxxxxxx> > +L: linux-kernel@xxxxxxxxxxxxxxx > +S: Supported > +F: drivers/misc/rpmb-core.c > +F: include/linux/rpmb.h > + > RPMSG TTY DRIVER > M: Arnaud Pouliquen <arnaud.pouliquen@xxxxxxxxxxx> > L: linux-remoteproc@xxxxxxxxxxxxxxx > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index 4fb291f0bf7c..dbff9e8c3a03 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -104,6 +104,16 @@ config PHANTOM > If you choose to build module, its name will be phantom. If unsure, > say N here. > > +config RPMB > + tristate "RPMB partition interface" > + depends on MMC > + help > + Unified RPMB unit interface for RPMB capable devices such as eMMC and > + UFS. Provides interface for in-kernel security controllers to access > + RPMB unit. > + > + If unsure, select N. > + > config TIFM_CORE > tristate "TI Flash Media interface support" > depends on PCI > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index ea6ea5bbbc9c..8af058ad1df4 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ > obj-$(CONFIG_TIFM_CORE) += tifm_core.o > obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o > obj-$(CONFIG_PHANTOM) += phantom.o > +obj-$(CONFIG_RPMB) += rpmb-core.o > obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o > obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o > obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o > diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c > new file mode 100644 > index 000000000000..5479469c26f3 > --- /dev/null > +++ b/drivers/misc/rpmb-core.c > @@ -0,0 +1,232 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved. > + * Copyright(c) 2021 - 2024 Linaro Ltd. > + */ > +#include <linux/device.h> > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/list.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/rpmb.h> > +#include <linux/slab.h> > + > +static struct list_head rpmb_dev_list; > +static DEFINE_MUTEX(rpmb_mutex); > +static struct blocking_notifier_head rpmb_interface = > + BLOCKING_NOTIFIER_INIT(rpmb_interface); > + > +/** > + * rpmb_dev_get() - increase rpmb device ref counter > + * @rdev: rpmb device > + */ > +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) > +{ > + if (rdev) > + get_device(rdev->parent_dev); > + return rdev; > +} > +EXPORT_SYMBOL_GPL(rpmb_dev_get); > + > +/** > + * rpmb_dev_put() - decrease rpmb device ref counter > + * @rdev: rpmb device > + */ > +void rpmb_dev_put(struct rpmb_dev *rdev) > +{ > + if (rdev) > + put_device(rdev->parent_dev); > +} > +EXPORT_SYMBOL_GPL(rpmb_dev_put); > + > +/** > + * rpmb_route_frames() - route rpmb frames to rpmb device > + * @rdev: rpmb device > + * @req: rpmb request frames > + * @req_len: length of rpmb request frames in bytes > + * @rsp: rpmb response frames > + * @rsp_len: length of rpmb response frames in bytes > + * > + * Returns: < 0 on failure > + */ > +int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, > + unsigned int req_len, u8 *rsp, unsigned int rsp_len) > +{ > + if (!req || !req_len || !rsp || !rsp_len) > + return -EINVAL; > + > + return rdev->descr.route_frames(rdev->parent_dev, req, req_len, > + rsp, rsp_len); > +} > +EXPORT_SYMBOL_GPL(rpmb_route_frames); > + > +/** > + * rpmb_dev_find_device() - return first matching rpmb device > + * @data: data for the match function > + * @match: the matching function > + * > + * Iterate over registered RPMB devices, and call @match() for each passing > + * it the RPMB device and @data. > + * > + * The return value of @match() is checked for each call. If it returns > + * anything other 0, break and return the found RPMB device. > + * > + * It's the callers responsibility to call rpmb_dev_put() on the returned > + * device, when it's done with it. > + * > + * Returns: a matching rpmb device or NULL on failure > + */ > +struct rpmb_dev *rpmb_dev_find_device(const void *data, > + const struct rpmb_dev *start, > + int (*match)(struct rpmb_dev *rdev, > + const void *data)) > +{ > + struct rpmb_dev *rdev; > + struct list_head *pos; > + > + mutex_lock(&rpmb_mutex); > + if (start) > + pos = start->list_node.next; > + else > + pos = rpmb_dev_list.next; > + > + while (pos != &rpmb_dev_list) { > + rdev = container_of(pos, struct rpmb_dev, list_node); > + if (match(rdev, data)) { > + rpmb_dev_get(rdev); > + goto out; > + } > + pos = pos->next; > + } > + rdev = NULL; > + > +out: > + mutex_unlock(&rpmb_mutex); > + > + return rdev; > +} EXPORT_SYMBOL_GPL missing? > +/** > + * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem > + * @rdev: the rpmb device to unregister > + * > + * This function should be called from the release function of the > + * underlying device used when the RPMB device was registered. > + * > + * Returns: < 0 on failure > + */ > +int rpmb_dev_unregister(struct rpmb_dev *rdev) > +{ > + if (!rdev) > + return -EINVAL; > + > + mutex_lock(&rpmb_mutex); > + list_del(&rdev->list_node); > + mutex_unlock(&rpmb_mutex); > + kfree(rdev->descr.dev_id); > + kfree(rdev); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rpmb_dev_unregister); > + > +/** > + * rpmb_dev_register - register RPMB partition with the RPMB subsystem > + * @dev: storage device of the rpmb device > + * @ops: device specific operations > + * > + * While registering the RPMB partition extract needed device information > + * while needed resources are available. > + * > + * Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure > + */ > +struct rpmb_dev *rpmb_dev_register(struct device *dev, > + struct rpmb_descr *descr) > +{ > + struct rpmb_dev *rdev; > + > + if (!dev || !descr || !descr->route_frames || !descr->dev_id || > + !descr->dev_id_len) > + return ERR_PTR(-EINVAL); > + > + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); > + if (!rdev) > + return ERR_PTR(-ENOMEM); > + rdev->descr = *descr; > + rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len, > + GFP_KERNEL); > + if (!rdev->descr.dev_id) { > + kfree(rdev); > + return ERR_PTR(-ENOMEM); > + } > + > + rdev->parent_dev = dev; > + > + dev_dbg(rdev->parent_dev, "registered device\n"); > + > + mutex_lock(&rpmb_mutex); > + list_add_tail(&rdev->list_node, &rpmb_dev_list); > + blocking_notifier_call_chain(&rpmb_interface, RPMB_NOTIFY_ADD_DEVICE, > + rdev); > + mutex_unlock(&rpmb_mutex); > + > + return rdev; > +} > +EXPORT_SYMBOL_GPL(rpmb_dev_register); > + > +/** > + * rpmb_interface_register() - register for new device notifications > + * > + * @nb : New entry in notifier chain > + * > + * Returns: 0 on success -EEXIST on error. > + */ > +int rpmb_interface_register(struct notifier_block *nb) > +{ > + struct rpmb_dev *rdev; > + int ret; > + > + ret = blocking_notifier_chain_register(&rpmb_interface, nb); > + if (ret) > + return ret; > + > + mutex_lock(&rpmb_mutex); > + list_for_each_entry(rdev, &rpmb_dev_list, list_node) > + nb->notifier_call(nb, RPMB_NOTIFY_ADD_DEVICE, rdev); > + mutex_unlock(&rpmb_mutex); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(rpmb_interface_register); > + > +/** > + * rpmb_interface_unregister() - unregister from new device notifications > + * > + * @nb : Entry to remove from notifier chain > + * > + * Returns: 0 on success or -ENOENT on failure. > + */ > +int rpmb_interface_unregister(struct notifier_block *nb) > +{ > + return blocking_notifier_chain_unregister(&rpmb_interface, nb); > +} > +EXPORT_SYMBOL_GPL(rpmb_interface_unregister); > + > +static int __init rpmb_init(void) > +{ > + INIT_LIST_HEAD(&rpmb_dev_list); > + return 0; > +} > + > +static void __exit rpmb_exit(void) > +{ > + mutex_destroy(&rpmb_mutex); > +} > + > +subsys_initcall(rpmb_init); > +module_exit(rpmb_exit); > + > +MODULE_AUTHOR("Jens Wiklander <jens.wiklander@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("RPMB class"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h > new file mode 100644 > index 000000000000..3ced206fdc17 > --- /dev/null > +++ b/include/linux/rpmb.h > @@ -0,0 +1,136 @@ > +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ > +/* > + * Copyright (C) 2015-2019 Intel Corp. All rights reserved > + * Copyright (C) 2021-2022 Linaro Ltd > + */ > +#ifndef __RPMB_H__ > +#define __RPMB_H__ > + > +#include <linux/types.h> > +#include <linux/device.h> > +#include <linux/notifier.h> > + > +/** > + * enum rpmb_type - type of underlying storage technology > + * > + * @RPMB_TYPE_EMMC : emmc (JESD84-B50.1) > + * @RPMB_TYPE_UFS : UFS (JESD220) > + * @RPMB_TYPE_NVME : NVM Express > + */ > +enum rpmb_type { > + RPMB_TYPE_EMMC, > + RPMB_TYPE_UFS, > + RPMB_TYPE_NVME, > +}; > + > +/** > + * struct rpmb_descr - RPMB description provided by the underlying block device > + * > + * @type : block device type > + * @route_frames : routes frames to and from the RPMB device > + * @dev_id : unique device identifier read from the hardware > + * @dev_id_len : length of unique device identifier > + * @reliable_wr_count: number of sectors that can be written in one access > + * @capacity : capacity of the device in units of 128K > + * > + * @dev_id is intended to be used as input when deriving the authenticaion key. > + */ > +struct rpmb_descr { > + enum rpmb_type type; > + int (*route_frames)(struct device *dev, u8 *req, unsigned int req_len, > + u8 *resp, unsigned int resp_len); > + u8 *dev_id; > + size_t dev_id_len; > + u16 reliable_wr_count; > + u16 capacity; > +}; > + > +/** > + * struct rpmb_dev - device which can support RPMB partition > + * > + * @parent_dev : parent device > + * @list_node : linked list node > + * @descr : RPMB description > + */ > +struct rpmb_dev { > + struct device *parent_dev; > + struct list_head list_node; > + struct rpmb_descr descr; > +}; > + > +enum rpmb_interface_action { > + RPMB_NOTIFY_ADD_DEVICE, > +}; > + > +/** > + * struct rpmb_interface - subscribe to new RPMB devices > + * > + * @list_node : linked list node > + * @add_rdev : notifies that a new RPMB device has been found > + */ > +struct rpmb_interface { > + struct list_head list_node; > + void (*add_rdev)(struct rpmb_interface *intf, struct rpmb_dev *rdev); > +}; > + > +#if IS_ENABLED(CONFIG_RPMB) > +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev); > +void rpmb_dev_put(struct rpmb_dev *rdev); > +struct rpmb_dev *rpmb_dev_find_device(const void *data, > + const struct rpmb_dev *start, > + int (*match)(struct rpmb_dev *rdev, > + const void *data)); > +struct rpmb_dev *rpmb_dev_register(struct device *dev, > + struct rpmb_descr *descr); > +int rpmb_dev_unregister(struct rpmb_dev *rdev); > + > +int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, > + unsigned int req_len, u8 *resp, unsigned int resp_len); > + > +int rpmb_interface_register(struct notifier_block *nb); > +int rpmb_interface_unregister(struct notifier_block *nb); > +#else > +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) > +{ > + return NULL; > +} > + > +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { } > + > +static inline struct rpmb_dev * > +rpmb_dev_find_device(const void *data, const struct rpmb_dev *start, > + int (*match)(struct rpmb_dev *rdev, const void *data)) > +{ > + return NULL; > +} > + > +static inline struct rpmb_dev * > +rpmb_dev_register(struct device *dev, const struct rpmb_ops *ops) > +{ > + return NULL; > +} > + > +static inline int rpmb_dev_unregister(struct rpmb_dev *dev) > +{ > + return 0; > +} > + > +static inline int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, > + unsigned int req_len, u8 *resp, > + unsigned int resp_len) > +{ > + return -EOPNOTSUPP; > +} > + > +static inline int rpmb_interface_register(struct notifier_block *nb) > +{ > + return -EOPNOTSUPP; > +} > + > +static inline int rpmb_interface_unregister(struct notifier_block *nb) > +{ > + return -EOPNOTSUPP; > +} > +#endif /* CONFIG_RPMB */ > + > +#endif /* __RPMB_H__ */ > -- > 2.34.1 >