232 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 15static DEFINE_IDA(rpmb_ida);16static DEFINE_MUTEX(rpmb_mutex);17 18/**19 * rpmb_dev_get() - increase rpmb device ref counter20 * @rdev: rpmb device21 */22struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)23{24 if (rdev)25 get_device(&rdev->dev);26 return rdev;27}28EXPORT_SYMBOL_GPL(rpmb_dev_get);29 30/**31 * rpmb_dev_put() - decrease rpmb device ref counter32 * @rdev: rpmb device33 */34void rpmb_dev_put(struct rpmb_dev *rdev)35{36 if (rdev)37 put_device(&rdev->dev);38}39EXPORT_SYMBOL_GPL(rpmb_dev_put);40 41/**42 * rpmb_route_frames() - route rpmb frames to rpmb device43 * @rdev: rpmb device44 * @req: rpmb request frames45 * @req_len: length of rpmb request frames in bytes46 * @rsp: rpmb response frames47 * @rsp_len: length of rpmb response frames in bytes48 *49 * Returns: < 0 on failure50 */51int 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}60EXPORT_SYMBOL_GPL(rpmb_route_frames);61 62static 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 73static 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 device80 * @start: rpmb device to begin with81 * @data: data for the match function82 * @match: the matching function83 *84 * Iterate over registered RPMB devices, and call @match() for each passing85 * it the RPMB device and @data.86 *87 * The return value of @match() is checked for each call. If it returns88 * anything other 0, break and return the found RPMB device.89 *90 * It's the callers responsibility to call rpmb_dev_put() on the returned91 * device, when it's done with it.92 *93 * Returns: a matching rpmb device or NULL on failure94 */95struct 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}109EXPORT_SYMBOL_GPL(rpmb_dev_find_device);110 111int rpmb_interface_register(struct class_interface *intf)112{113 intf->class = &rpmb_class;114 115 return class_interface_register(intf);116}117EXPORT_SYMBOL_GPL(rpmb_interface_register);118 119void rpmb_interface_unregister(struct class_interface *intf)120{121 class_interface_unregister(intf);122}123EXPORT_SYMBOL_GPL(rpmb_interface_unregister);124 125/**126 * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem127 * @rdev: the rpmb device to unregister128 *129 * This function should be called from the release function of the130 * underlying device used when the RPMB device was registered.131 *132 * Returns: < 0 on failure133 */134int 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}145EXPORT_SYMBOL_GPL(rpmb_dev_unregister);146 147/**148 * rpmb_dev_register - register RPMB partition with the RPMB subsystem149 * @dev: storage device of the rpmb device150 * @descr: RPMB device description151 *152 * While registering the RPMB partition extract needed device information153 * while needed resources are available.154 *155 * Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure156 */157struct 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 put_device(&rdev->dev);192 return ERR_PTR(ret);193 }194 195 dev_dbg(&rdev->dev, "registered device\n");196 197 return rdev;198 199err_free_dev_id:200 kfree(rdev->descr.dev_id);201err_free_rdev:202 kfree(rdev);203 return ERR_PTR(ret);204}205EXPORT_SYMBOL_GPL(rpmb_dev_register);206 207static int __init rpmb_init(void)208{209 int ret;210 211 ret = class_register(&rpmb_class);212 if (ret) {213 pr_err("couldn't create class\n");214 return ret;215 }216 ida_init(&rpmb_ida);217 return 0;218}219 220static void __exit rpmb_exit(void)221{222 ida_destroy(&rpmb_ida);223 class_unregister(&rpmb_class);224}225 226subsys_initcall(rpmb_init);227module_exit(rpmb_exit);228 229MODULE_AUTHOR("Jens Wiklander <jens.wiklander@linaro.org>");230MODULE_DESCRIPTION("RPMB class");231MODULE_LICENSE("GPL");232