xref: /linux/drivers/misc/rpmb-core.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
11e9046e3SJens Wiklander // SPDX-License-Identifier: GPL-2.0
21e9046e3SJens Wiklander /*
31e9046e3SJens Wiklander  * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
41e9046e3SJens Wiklander  * Copyright(c) 2021 - 2024 Linaro Ltd.
51e9046e3SJens Wiklander  */
61e9046e3SJens Wiklander #include <linux/device.h>
71e9046e3SJens Wiklander #include <linux/init.h>
81e9046e3SJens Wiklander #include <linux/kernel.h>
91e9046e3SJens Wiklander #include <linux/list.h>
101e9046e3SJens Wiklander #include <linux/module.h>
111e9046e3SJens Wiklander #include <linux/mutex.h>
121e9046e3SJens Wiklander #include <linux/rpmb.h>
131e9046e3SJens Wiklander #include <linux/slab.h>
141e9046e3SJens Wiklander 
151e9046e3SJens Wiklander static DEFINE_IDA(rpmb_ida);
161e9046e3SJens Wiklander static DEFINE_MUTEX(rpmb_mutex);
171e9046e3SJens Wiklander 
181e9046e3SJens Wiklander /**
191e9046e3SJens Wiklander  * rpmb_dev_get() - increase rpmb device ref counter
201e9046e3SJens Wiklander  * @rdev: rpmb device
211e9046e3SJens Wiklander  */
rpmb_dev_get(struct rpmb_dev * rdev)221e9046e3SJens Wiklander struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
231e9046e3SJens Wiklander {
241e9046e3SJens Wiklander 	if (rdev)
251e9046e3SJens Wiklander 		get_device(&rdev->dev);
261e9046e3SJens Wiklander 	return rdev;
271e9046e3SJens Wiklander }
281e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_dev_get);
291e9046e3SJens Wiklander 
301e9046e3SJens Wiklander /**
311e9046e3SJens Wiklander  * rpmb_dev_put() - decrease rpmb device ref counter
321e9046e3SJens Wiklander  * @rdev: rpmb device
331e9046e3SJens Wiklander  */
rpmb_dev_put(struct rpmb_dev * rdev)341e9046e3SJens Wiklander void rpmb_dev_put(struct rpmb_dev *rdev)
351e9046e3SJens Wiklander {
361e9046e3SJens Wiklander 	if (rdev)
371e9046e3SJens Wiklander 		put_device(&rdev->dev);
381e9046e3SJens Wiklander }
391e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_dev_put);
401e9046e3SJens Wiklander 
411e9046e3SJens Wiklander /**
421e9046e3SJens Wiklander  * rpmb_route_frames() - route rpmb frames to rpmb device
431e9046e3SJens Wiklander  * @rdev:	rpmb device
441e9046e3SJens Wiklander  * @req:	rpmb request frames
451e9046e3SJens Wiklander  * @req_len:	length of rpmb request frames in bytes
461e9046e3SJens Wiklander  * @rsp:	rpmb response frames
471e9046e3SJens Wiklander  * @rsp_len:	length of rpmb response frames in bytes
481e9046e3SJens Wiklander  *
491e9046e3SJens Wiklander  * Returns: < 0 on failure
501e9046e3SJens Wiklander  */
rpmb_route_frames(struct rpmb_dev * rdev,u8 * req,unsigned int req_len,u8 * rsp,unsigned int rsp_len)511e9046e3SJens Wiklander int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req,
521e9046e3SJens Wiklander 		      unsigned int req_len, u8 *rsp, unsigned int rsp_len)
531e9046e3SJens Wiklander {
541e9046e3SJens Wiklander 	if (!req || !req_len || !rsp || !rsp_len)
551e9046e3SJens Wiklander 		return -EINVAL;
561e9046e3SJens Wiklander 
571e9046e3SJens Wiklander 	return rdev->descr.route_frames(rdev->dev.parent, req, req_len,
581e9046e3SJens Wiklander 					rsp, rsp_len);
591e9046e3SJens Wiklander }
601e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_route_frames);
611e9046e3SJens Wiklander 
rpmb_dev_release(struct device * dev)621e9046e3SJens Wiklander static void rpmb_dev_release(struct device *dev)
631e9046e3SJens Wiklander {
641e9046e3SJens Wiklander 	struct rpmb_dev *rdev = to_rpmb_dev(dev);
651e9046e3SJens Wiklander 
661e9046e3SJens Wiklander 	mutex_lock(&rpmb_mutex);
671e9046e3SJens Wiklander 	ida_simple_remove(&rpmb_ida, rdev->id);
681e9046e3SJens Wiklander 	mutex_unlock(&rpmb_mutex);
691e9046e3SJens Wiklander 	kfree(rdev->descr.dev_id);
701e9046e3SJens Wiklander 	kfree(rdev);
711e9046e3SJens Wiklander }
721e9046e3SJens Wiklander 
731e9046e3SJens Wiklander static struct class rpmb_class = {
741e9046e3SJens Wiklander 	.name = "rpmb",
751e9046e3SJens Wiklander 	.dev_release = rpmb_dev_release,
761e9046e3SJens Wiklander };
771e9046e3SJens Wiklander 
781e9046e3SJens Wiklander /**
791e9046e3SJens Wiklander  * rpmb_dev_find_device() - return first matching rpmb device
801e9046e3SJens Wiklander  * @start: rpmb device to begin with
811e9046e3SJens Wiklander  * @data: data for the match function
821e9046e3SJens Wiklander  * @match: the matching function
831e9046e3SJens Wiklander  *
841e9046e3SJens Wiklander  * Iterate over registered RPMB devices, and call @match() for each passing
851e9046e3SJens Wiklander  * it the RPMB device and @data.
861e9046e3SJens Wiklander  *
871e9046e3SJens Wiklander  * The return value of @match() is checked for each call. If it returns
881e9046e3SJens Wiklander  * anything other 0, break and return the found RPMB device.
891e9046e3SJens Wiklander  *
901e9046e3SJens Wiklander  * It's the callers responsibility to call rpmb_dev_put() on the returned
911e9046e3SJens Wiklander  * device, when it's done with it.
921e9046e3SJens Wiklander  *
931e9046e3SJens Wiklander  * Returns: a matching rpmb device or NULL on failure
941e9046e3SJens Wiklander  */
rpmb_dev_find_device(const void * data,const struct rpmb_dev * start,int (* match)(struct device * dev,const void * data))951e9046e3SJens Wiklander struct rpmb_dev *rpmb_dev_find_device(const void *data,
961e9046e3SJens Wiklander 				      const struct rpmb_dev *start,
971e9046e3SJens Wiklander 				      int (*match)(struct device *dev,
981e9046e3SJens Wiklander 						   const void *data))
991e9046e3SJens Wiklander {
1001e9046e3SJens Wiklander 	struct device *dev;
1011e9046e3SJens Wiklander 	const struct device *start_dev = NULL;
1021e9046e3SJens Wiklander 
1031e9046e3SJens Wiklander 	if (start)
1041e9046e3SJens Wiklander 		start_dev = &start->dev;
1051e9046e3SJens Wiklander 	dev = class_find_device(&rpmb_class, start_dev, data, match);
1061e9046e3SJens Wiklander 
1071e9046e3SJens Wiklander 	return dev ? to_rpmb_dev(dev) : NULL;
1081e9046e3SJens Wiklander }
1091e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_dev_find_device);
1101e9046e3SJens Wiklander 
rpmb_interface_register(struct class_interface * intf)1111e9046e3SJens Wiklander int rpmb_interface_register(struct class_interface *intf)
1121e9046e3SJens Wiklander {
1131e9046e3SJens Wiklander 	intf->class = &rpmb_class;
1141e9046e3SJens Wiklander 
1151e9046e3SJens Wiklander 	return class_interface_register(intf);
1161e9046e3SJens Wiklander }
1171e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_interface_register);
1181e9046e3SJens Wiklander 
rpmb_interface_unregister(struct class_interface * intf)1191e9046e3SJens Wiklander void rpmb_interface_unregister(struct class_interface *intf)
1201e9046e3SJens Wiklander {
1211e9046e3SJens Wiklander 	class_interface_unregister(intf);
1221e9046e3SJens Wiklander }
1231e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_interface_unregister);
1241e9046e3SJens Wiklander 
1251e9046e3SJens Wiklander /**
1261e9046e3SJens Wiklander  * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
1271e9046e3SJens Wiklander  * @rdev: the rpmb device to unregister
1281e9046e3SJens Wiklander  *
1291e9046e3SJens Wiklander  * This function should be called from the release function of the
1301e9046e3SJens Wiklander  * underlying device used when the RPMB device was registered.
1311e9046e3SJens Wiklander  *
1321e9046e3SJens Wiklander  * Returns: < 0 on failure
1331e9046e3SJens Wiklander  */
rpmb_dev_unregister(struct rpmb_dev * rdev)1341e9046e3SJens Wiklander int rpmb_dev_unregister(struct rpmb_dev *rdev)
1351e9046e3SJens Wiklander {
1361e9046e3SJens Wiklander 	if (!rdev)
1371e9046e3SJens Wiklander 		return -EINVAL;
1381e9046e3SJens Wiklander 
1391e9046e3SJens Wiklander 	device_del(&rdev->dev);
1401e9046e3SJens Wiklander 
1411e9046e3SJens Wiklander 	rpmb_dev_put(rdev);
1421e9046e3SJens Wiklander 
1431e9046e3SJens Wiklander 	return 0;
1441e9046e3SJens Wiklander }
1451e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
1461e9046e3SJens Wiklander 
1471e9046e3SJens Wiklander /**
1481e9046e3SJens Wiklander  * rpmb_dev_register - register RPMB partition with the RPMB subsystem
1491e9046e3SJens Wiklander  * @dev: storage device of the rpmb device
1501e9046e3SJens Wiklander  * @descr: RPMB device description
1511e9046e3SJens Wiklander  *
1521e9046e3SJens Wiklander  * While registering the RPMB partition extract needed device information
1531e9046e3SJens Wiklander  * while needed resources are available.
1541e9046e3SJens Wiklander  *
1551e9046e3SJens Wiklander  * Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure
1561e9046e3SJens Wiklander  */
rpmb_dev_register(struct device * dev,struct rpmb_descr * descr)1571e9046e3SJens Wiklander struct rpmb_dev *rpmb_dev_register(struct device *dev,
1581e9046e3SJens Wiklander 				   struct rpmb_descr *descr)
1591e9046e3SJens Wiklander {
1601e9046e3SJens Wiklander 	struct rpmb_dev *rdev;
1611e9046e3SJens Wiklander 	int ret;
1621e9046e3SJens Wiklander 
1631e9046e3SJens Wiklander 	if (!dev || !descr || !descr->route_frames || !descr->dev_id ||
1641e9046e3SJens Wiklander 	    !descr->dev_id_len)
1651e9046e3SJens Wiklander 		return ERR_PTR(-EINVAL);
1661e9046e3SJens Wiklander 
1671e9046e3SJens Wiklander 	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
1681e9046e3SJens Wiklander 	if (!rdev)
1691e9046e3SJens Wiklander 		return ERR_PTR(-ENOMEM);
1701e9046e3SJens Wiklander 	rdev->descr = *descr;
1711e9046e3SJens Wiklander 	rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len,
1721e9046e3SJens Wiklander 				     GFP_KERNEL);
1731e9046e3SJens Wiklander 	if (!rdev->descr.dev_id) {
1741e9046e3SJens Wiklander 		ret = -ENOMEM;
1751e9046e3SJens Wiklander 		goto err_free_rdev;
1761e9046e3SJens Wiklander 	}
1771e9046e3SJens Wiklander 
1781e9046e3SJens Wiklander 	mutex_lock(&rpmb_mutex);
1791e9046e3SJens Wiklander 	ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
1801e9046e3SJens Wiklander 	mutex_unlock(&rpmb_mutex);
1811e9046e3SJens Wiklander 	if (ret < 0)
1821e9046e3SJens Wiklander 		goto err_free_dev_id;
1831e9046e3SJens Wiklander 	rdev->id = ret;
1841e9046e3SJens Wiklander 
1851e9046e3SJens Wiklander 	dev_set_name(&rdev->dev, "rpmb%d", rdev->id);
1861e9046e3SJens Wiklander 	rdev->dev.class = &rpmb_class;
1871e9046e3SJens Wiklander 	rdev->dev.parent = dev;
1881e9046e3SJens Wiklander 
1891e9046e3SJens Wiklander 	ret = device_register(&rdev->dev);
190*5854809bSJens Wiklander 	if (ret) {
191*5854809bSJens Wiklander 		put_device(&rdev->dev);
192*5854809bSJens Wiklander 		return ERR_PTR(ret);
193*5854809bSJens Wiklander 	}
1941e9046e3SJens Wiklander 
1951e9046e3SJens Wiklander 	dev_dbg(&rdev->dev, "registered device\n");
1961e9046e3SJens Wiklander 
1971e9046e3SJens Wiklander 	return rdev;
1981e9046e3SJens Wiklander 
1991e9046e3SJens Wiklander err_free_dev_id:
2001e9046e3SJens Wiklander 	kfree(rdev->descr.dev_id);
2011e9046e3SJens Wiklander err_free_rdev:
2021e9046e3SJens Wiklander 	kfree(rdev);
2031e9046e3SJens Wiklander 	return ERR_PTR(ret);
2041e9046e3SJens Wiklander }
2051e9046e3SJens Wiklander EXPORT_SYMBOL_GPL(rpmb_dev_register);
2061e9046e3SJens Wiklander 
rpmb_init(void)2071e9046e3SJens Wiklander static int __init rpmb_init(void)
2081e9046e3SJens Wiklander {
2091e9046e3SJens Wiklander 	int ret;
2101e9046e3SJens Wiklander 
2111e9046e3SJens Wiklander 	ret = class_register(&rpmb_class);
2121e9046e3SJens Wiklander 	if (ret) {
2131e9046e3SJens Wiklander 		pr_err("couldn't create class\n");
2141e9046e3SJens Wiklander 		return ret;
2151e9046e3SJens Wiklander 	}
2161e9046e3SJens Wiklander 	ida_init(&rpmb_ida);
2171e9046e3SJens Wiklander 	return 0;
2181e9046e3SJens Wiklander }
2191e9046e3SJens Wiklander 
rpmb_exit(void)2201e9046e3SJens Wiklander static void __exit rpmb_exit(void)
2211e9046e3SJens Wiklander {
2221e9046e3SJens Wiklander 	ida_destroy(&rpmb_ida);
2231e9046e3SJens Wiklander 	class_unregister(&rpmb_class);
2241e9046e3SJens Wiklander }
2251e9046e3SJens Wiklander 
2261e9046e3SJens Wiklander subsys_initcall(rpmb_init);
2271e9046e3SJens Wiklander module_exit(rpmb_exit);
2281e9046e3SJens Wiklander 
2291e9046e3SJens Wiklander MODULE_AUTHOR("Jens Wiklander <jens.wiklander@linaro.org>");
2301e9046e3SJens Wiklander MODULE_DESCRIPTION("RPMB class");
2311e9046e3SJens Wiklander MODULE_LICENSE("GPL");
232