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 */ 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 */ 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 */ 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 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 */ 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 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 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 */ 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 */ 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 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 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