11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2dbaf0624SGonglei /* Management for virtio crypto devices (refer to adf_dev_mgr.c)
3dbaf0624SGonglei *
4dbaf0624SGonglei * Copyright 2016 HUAWEI TECHNOLOGIES CO., LTD.
5dbaf0624SGonglei */
6dbaf0624SGonglei
7dbaf0624SGonglei #include <linux/mutex.h>
8dbaf0624SGonglei #include <linux/list.h>
9dbaf0624SGonglei #include <linux/module.h>
10dbaf0624SGonglei
11dbaf0624SGonglei #include <uapi/linux/virtio_crypto.h>
12dbaf0624SGonglei #include "virtio_crypto_common.h"
13dbaf0624SGonglei
14dbaf0624SGonglei static LIST_HEAD(virtio_crypto_table);
15dbaf0624SGonglei static uint32_t num_devices;
16dbaf0624SGonglei
17dbaf0624SGonglei /* The table_lock protects the above global list and num_devices */
18dbaf0624SGonglei static DEFINE_MUTEX(table_lock);
19dbaf0624SGonglei
20dbaf0624SGonglei #define VIRTIO_CRYPTO_MAX_DEVICES 32
21dbaf0624SGonglei
22dbaf0624SGonglei
23dbaf0624SGonglei /*
24dbaf0624SGonglei * virtcrypto_devmgr_add_dev() - Add vcrypto_dev to the acceleration
25dbaf0624SGonglei * framework.
26dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
27dbaf0624SGonglei *
28dbaf0624SGonglei * Function adds virtio crypto device to the global list.
29dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
30dbaf0624SGonglei *
31dbaf0624SGonglei * Return: 0 on success, error code othewise.
32dbaf0624SGonglei */
virtcrypto_devmgr_add_dev(struct virtio_crypto * vcrypto_dev)33dbaf0624SGonglei int virtcrypto_devmgr_add_dev(struct virtio_crypto *vcrypto_dev)
34dbaf0624SGonglei {
35dbaf0624SGonglei struct list_head *itr;
36dbaf0624SGonglei
37dbaf0624SGonglei mutex_lock(&table_lock);
38dbaf0624SGonglei if (num_devices == VIRTIO_CRYPTO_MAX_DEVICES) {
39dbaf0624SGonglei pr_info("virtio_crypto: only support up to %d devices\n",
40dbaf0624SGonglei VIRTIO_CRYPTO_MAX_DEVICES);
41dbaf0624SGonglei mutex_unlock(&table_lock);
42dbaf0624SGonglei return -EFAULT;
43dbaf0624SGonglei }
44dbaf0624SGonglei
45dbaf0624SGonglei list_for_each(itr, &virtio_crypto_table) {
46dbaf0624SGonglei struct virtio_crypto *ptr =
47dbaf0624SGonglei list_entry(itr, struct virtio_crypto, list);
48dbaf0624SGonglei
49dbaf0624SGonglei if (ptr == vcrypto_dev) {
50dbaf0624SGonglei mutex_unlock(&table_lock);
51dbaf0624SGonglei return -EEXIST;
52dbaf0624SGonglei }
53dbaf0624SGonglei }
54dbaf0624SGonglei atomic_set(&vcrypto_dev->ref_count, 0);
55dbaf0624SGonglei list_add_tail(&vcrypto_dev->list, &virtio_crypto_table);
56dbaf0624SGonglei vcrypto_dev->dev_id = num_devices++;
57dbaf0624SGonglei mutex_unlock(&table_lock);
58dbaf0624SGonglei return 0;
59dbaf0624SGonglei }
60dbaf0624SGonglei
virtcrypto_devmgr_get_head(void)61dbaf0624SGonglei struct list_head *virtcrypto_devmgr_get_head(void)
62dbaf0624SGonglei {
63dbaf0624SGonglei return &virtio_crypto_table;
64dbaf0624SGonglei }
65dbaf0624SGonglei
66dbaf0624SGonglei /*
67dbaf0624SGonglei * virtcrypto_devmgr_rm_dev() - Remove vcrypto_dev from the acceleration
68dbaf0624SGonglei * framework.
69dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
70dbaf0624SGonglei *
71dbaf0624SGonglei * Function removes virtio crypto device from the acceleration framework.
72dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
73dbaf0624SGonglei *
74dbaf0624SGonglei * Return: void
75dbaf0624SGonglei */
virtcrypto_devmgr_rm_dev(struct virtio_crypto * vcrypto_dev)76dbaf0624SGonglei void virtcrypto_devmgr_rm_dev(struct virtio_crypto *vcrypto_dev)
77dbaf0624SGonglei {
78dbaf0624SGonglei mutex_lock(&table_lock);
79dbaf0624SGonglei list_del(&vcrypto_dev->list);
80dbaf0624SGonglei num_devices--;
81dbaf0624SGonglei mutex_unlock(&table_lock);
82dbaf0624SGonglei }
83dbaf0624SGonglei
84dbaf0624SGonglei /*
85dbaf0624SGonglei * virtcrypto_devmgr_get_first()
86dbaf0624SGonglei *
87dbaf0624SGonglei * Function returns the first virtio crypto device from the acceleration
88dbaf0624SGonglei * framework.
89dbaf0624SGonglei *
90dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
91dbaf0624SGonglei *
92dbaf0624SGonglei * Return: pointer to vcrypto_dev or NULL if not found.
93dbaf0624SGonglei */
virtcrypto_devmgr_get_first(void)94dbaf0624SGonglei struct virtio_crypto *virtcrypto_devmgr_get_first(void)
95dbaf0624SGonglei {
96dbaf0624SGonglei struct virtio_crypto *dev = NULL;
97dbaf0624SGonglei
98dbaf0624SGonglei mutex_lock(&table_lock);
99dbaf0624SGonglei if (!list_empty(&virtio_crypto_table))
100dbaf0624SGonglei dev = list_first_entry(&virtio_crypto_table,
101dbaf0624SGonglei struct virtio_crypto,
102dbaf0624SGonglei list);
103dbaf0624SGonglei mutex_unlock(&table_lock);
104dbaf0624SGonglei return dev;
105dbaf0624SGonglei }
106dbaf0624SGonglei
107dbaf0624SGonglei /*
108dbaf0624SGonglei * virtcrypto_dev_in_use() - Check whether vcrypto_dev is currently in use
109dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
110dbaf0624SGonglei *
111dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
112dbaf0624SGonglei *
113dbaf0624SGonglei * Return: 1 when device is in use, 0 otherwise.
114dbaf0624SGonglei */
virtcrypto_dev_in_use(struct virtio_crypto * vcrypto_dev)115dbaf0624SGonglei int virtcrypto_dev_in_use(struct virtio_crypto *vcrypto_dev)
116dbaf0624SGonglei {
117dbaf0624SGonglei return atomic_read(&vcrypto_dev->ref_count) != 0;
118dbaf0624SGonglei }
119dbaf0624SGonglei
120dbaf0624SGonglei /*
121dbaf0624SGonglei * virtcrypto_dev_get() - Increment vcrypto_dev reference count
122dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
123dbaf0624SGonglei *
124dbaf0624SGonglei * Increment the vcrypto_dev refcount and if this is the first time
125dbaf0624SGonglei * incrementing it during this period the vcrypto_dev is in use,
126dbaf0624SGonglei * increment the module refcount too.
127dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
128dbaf0624SGonglei *
129dbaf0624SGonglei * Return: 0 when successful, EFAULT when fail to bump module refcount
130dbaf0624SGonglei */
virtcrypto_dev_get(struct virtio_crypto * vcrypto_dev)131dbaf0624SGonglei int virtcrypto_dev_get(struct virtio_crypto *vcrypto_dev)
132dbaf0624SGonglei {
133dbaf0624SGonglei if (atomic_add_return(1, &vcrypto_dev->ref_count) == 1)
134dbaf0624SGonglei if (!try_module_get(vcrypto_dev->owner))
135dbaf0624SGonglei return -EFAULT;
136dbaf0624SGonglei return 0;
137dbaf0624SGonglei }
138dbaf0624SGonglei
139dbaf0624SGonglei /*
140dbaf0624SGonglei * virtcrypto_dev_put() - Decrement vcrypto_dev reference count
141dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
142dbaf0624SGonglei *
143dbaf0624SGonglei * Decrement the vcrypto_dev refcount and if this is the last time
144dbaf0624SGonglei * decrementing it during this period the vcrypto_dev is in use,
145dbaf0624SGonglei * decrement the module refcount too.
146dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
147dbaf0624SGonglei *
148dbaf0624SGonglei * Return: void
149dbaf0624SGonglei */
virtcrypto_dev_put(struct virtio_crypto * vcrypto_dev)150dbaf0624SGonglei void virtcrypto_dev_put(struct virtio_crypto *vcrypto_dev)
151dbaf0624SGonglei {
152dbaf0624SGonglei if (atomic_sub_return(1, &vcrypto_dev->ref_count) == 0)
153dbaf0624SGonglei module_put(vcrypto_dev->owner);
154dbaf0624SGonglei }
155dbaf0624SGonglei
156dbaf0624SGonglei /*
157dbaf0624SGonglei * virtcrypto_dev_started() - Check whether device has started
158dbaf0624SGonglei * @vcrypto_dev: Pointer to virtio crypto device.
159dbaf0624SGonglei *
160dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
161dbaf0624SGonglei *
162dbaf0624SGonglei * Return: 1 when the device has started, 0 otherwise
163dbaf0624SGonglei */
virtcrypto_dev_started(struct virtio_crypto * vcrypto_dev)164dbaf0624SGonglei int virtcrypto_dev_started(struct virtio_crypto *vcrypto_dev)
165dbaf0624SGonglei {
166dbaf0624SGonglei return (vcrypto_dev->status & VIRTIO_CRYPTO_S_HW_READY);
167dbaf0624SGonglei }
168dbaf0624SGonglei
169dbaf0624SGonglei /*
170dbaf0624SGonglei * virtcrypto_get_dev_node() - Get vcrypto_dev on the node.
171dbaf0624SGonglei * @node: Node id the driver works.
172d0d859bbSFarhan Ali * @service: Crypto service that needs to be supported by the
173d0d859bbSFarhan Ali * dev
174d0d859bbSFarhan Ali * @algo: The algorithm number that needs to be supported by the
175d0d859bbSFarhan Ali * dev
176dbaf0624SGonglei *
177d0d859bbSFarhan Ali * Function returns the virtio crypto device used fewest on the node,
178d0d859bbSFarhan Ali * and supports the given crypto service and algorithm.
179dbaf0624SGonglei *
180dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
181dbaf0624SGonglei *
182dbaf0624SGonglei * Return: pointer to vcrypto_dev or NULL if not found.
183dbaf0624SGonglei */
virtcrypto_get_dev_node(int node,uint32_t service,uint32_t algo)184d0d859bbSFarhan Ali struct virtio_crypto *virtcrypto_get_dev_node(int node, uint32_t service,
185d0d859bbSFarhan Ali uint32_t algo)
186dbaf0624SGonglei {
187dbaf0624SGonglei struct virtio_crypto *vcrypto_dev = NULL, *tmp_dev;
188dbaf0624SGonglei unsigned long best = ~0;
189dbaf0624SGonglei unsigned long ctr;
190dbaf0624SGonglei
191dbaf0624SGonglei mutex_lock(&table_lock);
192dbaf0624SGonglei list_for_each_entry(tmp_dev, virtcrypto_devmgr_get_head(), list) {
193dbaf0624SGonglei
194dbaf0624SGonglei if ((node == dev_to_node(&tmp_dev->vdev->dev) ||
195dbaf0624SGonglei dev_to_node(&tmp_dev->vdev->dev) < 0) &&
196d0d859bbSFarhan Ali virtcrypto_dev_started(tmp_dev) &&
197d0d859bbSFarhan Ali virtcrypto_algo_is_supported(tmp_dev, service, algo)) {
198dbaf0624SGonglei ctr = atomic_read(&tmp_dev->ref_count);
199dbaf0624SGonglei if (best > ctr) {
200dbaf0624SGonglei vcrypto_dev = tmp_dev;
201dbaf0624SGonglei best = ctr;
202dbaf0624SGonglei }
203dbaf0624SGonglei }
204dbaf0624SGonglei }
205dbaf0624SGonglei
206dbaf0624SGonglei if (!vcrypto_dev) {
207dbaf0624SGonglei pr_info("virtio_crypto: Could not find a device on node %d\n",
208dbaf0624SGonglei node);
209dbaf0624SGonglei /* Get any started device */
210dbaf0624SGonglei list_for_each_entry(tmp_dev,
211dbaf0624SGonglei virtcrypto_devmgr_get_head(), list) {
212d0d859bbSFarhan Ali if (virtcrypto_dev_started(tmp_dev) &&
213d0d859bbSFarhan Ali virtcrypto_algo_is_supported(tmp_dev,
214d0d859bbSFarhan Ali service, algo)) {
215dbaf0624SGonglei vcrypto_dev = tmp_dev;
216dbaf0624SGonglei break;
217dbaf0624SGonglei }
218dbaf0624SGonglei }
219dbaf0624SGonglei }
220dbaf0624SGonglei mutex_unlock(&table_lock);
221dbaf0624SGonglei if (!vcrypto_dev)
222dbaf0624SGonglei return NULL;
223dbaf0624SGonglei
224dbaf0624SGonglei virtcrypto_dev_get(vcrypto_dev);
225dbaf0624SGonglei return vcrypto_dev;
226dbaf0624SGonglei }
227dbaf0624SGonglei
228dbaf0624SGonglei /*
229dbaf0624SGonglei * virtcrypto_dev_start() - Start virtio crypto device
230dbaf0624SGonglei * @vcrypto: Pointer to virtio crypto device.
231dbaf0624SGonglei *
232dbaf0624SGonglei * Function notifies all the registered services that the virtio crypto device
233dbaf0624SGonglei * is ready to be used.
234dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
235dbaf0624SGonglei *
236dbaf0624SGonglei * Return: 0 on success, EFAULT when fail to register algorithms
237dbaf0624SGonglei */
virtcrypto_dev_start(struct virtio_crypto * vcrypto)238dbaf0624SGonglei int virtcrypto_dev_start(struct virtio_crypto *vcrypto)
239dbaf0624SGonglei {
240*ea993de1Szhenwei pi if (virtio_crypto_skcipher_algs_register(vcrypto)) {
241*ea993de1Szhenwei pi pr_err("virtio_crypto: Failed to register crypto skcipher algs\n");
242dbaf0624SGonglei return -EFAULT;
243dbaf0624SGonglei }
244dbaf0624SGonglei
24559ca6c93Szhenwei pi if (virtio_crypto_akcipher_algs_register(vcrypto)) {
24659ca6c93Szhenwei pi pr_err("virtio_crypto: Failed to register crypto akcipher algs\n");
247*ea993de1Szhenwei pi virtio_crypto_skcipher_algs_unregister(vcrypto);
24859ca6c93Szhenwei pi return -EFAULT;
24959ca6c93Szhenwei pi }
25059ca6c93Szhenwei pi
251dbaf0624SGonglei return 0;
252dbaf0624SGonglei }
253dbaf0624SGonglei
254dbaf0624SGonglei /*
255dbaf0624SGonglei * virtcrypto_dev_stop() - Stop virtio crypto device
256dbaf0624SGonglei * @vcrypto: Pointer to virtio crypto device.
257dbaf0624SGonglei *
258dbaf0624SGonglei * Function notifies all the registered services that the virtio crypto device
259dbaf0624SGonglei * is ready to be used.
260dbaf0624SGonglei * To be used by virtio crypto device specific drivers.
261dbaf0624SGonglei *
262dbaf0624SGonglei * Return: void
263dbaf0624SGonglei */
virtcrypto_dev_stop(struct virtio_crypto * vcrypto)264dbaf0624SGonglei void virtcrypto_dev_stop(struct virtio_crypto *vcrypto)
265dbaf0624SGonglei {
266*ea993de1Szhenwei pi virtio_crypto_skcipher_algs_unregister(vcrypto);
26759ca6c93Szhenwei pi virtio_crypto_akcipher_algs_unregister(vcrypto);
268d0d859bbSFarhan Ali }
269d0d859bbSFarhan Ali
270d0d859bbSFarhan Ali /*
271d0d859bbSFarhan Ali * vcrypto_algo_is_supported()
272d0d859bbSFarhan Ali * @vcrypto: Pointer to virtio crypto device.
273d0d859bbSFarhan Ali * @service: The bit number for service validate.
274d0d859bbSFarhan Ali * See VIRTIO_CRYPTO_SERVICE_*
275d0d859bbSFarhan Ali * @algo : The bit number for the algorithm to validate.
276d0d859bbSFarhan Ali *
277d0d859bbSFarhan Ali *
278d0d859bbSFarhan Ali * Validate if the virtio crypto device supports a service and
279d0d859bbSFarhan Ali * algo.
280d0d859bbSFarhan Ali *
281d0d859bbSFarhan Ali * Return true if device supports a service and algo.
282d0d859bbSFarhan Ali */
283d0d859bbSFarhan Ali
virtcrypto_algo_is_supported(struct virtio_crypto * vcrypto,uint32_t service,uint32_t algo)284d0d859bbSFarhan Ali bool virtcrypto_algo_is_supported(struct virtio_crypto *vcrypto,
285d0d859bbSFarhan Ali uint32_t service,
286d0d859bbSFarhan Ali uint32_t algo)
287d0d859bbSFarhan Ali {
288d0d859bbSFarhan Ali uint32_t service_mask = 1u << service;
289d0d859bbSFarhan Ali uint32_t algo_mask = 0;
290d0d859bbSFarhan Ali bool low = true;
291d0d859bbSFarhan Ali
292d0d859bbSFarhan Ali if (algo > 31) {
293d0d859bbSFarhan Ali algo -= 32;
294d0d859bbSFarhan Ali low = false;
295d0d859bbSFarhan Ali }
296d0d859bbSFarhan Ali
297d0d859bbSFarhan Ali if (!(vcrypto->crypto_services & service_mask))
298d0d859bbSFarhan Ali return false;
299d0d859bbSFarhan Ali
300d0d859bbSFarhan Ali switch (service) {
301d0d859bbSFarhan Ali case VIRTIO_CRYPTO_SERVICE_CIPHER:
302d0d859bbSFarhan Ali if (low)
303d0d859bbSFarhan Ali algo_mask = vcrypto->cipher_algo_l;
304d0d859bbSFarhan Ali else
305d0d859bbSFarhan Ali algo_mask = vcrypto->cipher_algo_h;
306d0d859bbSFarhan Ali break;
307d0d859bbSFarhan Ali
308d0d859bbSFarhan Ali case VIRTIO_CRYPTO_SERVICE_HASH:
309d0d859bbSFarhan Ali algo_mask = vcrypto->hash_algo;
310d0d859bbSFarhan Ali break;
311d0d859bbSFarhan Ali
312d0d859bbSFarhan Ali case VIRTIO_CRYPTO_SERVICE_MAC:
313d0d859bbSFarhan Ali if (low)
314d0d859bbSFarhan Ali algo_mask = vcrypto->mac_algo_l;
315d0d859bbSFarhan Ali else
316d0d859bbSFarhan Ali algo_mask = vcrypto->mac_algo_h;
317d0d859bbSFarhan Ali break;
318d0d859bbSFarhan Ali
319d0d859bbSFarhan Ali case VIRTIO_CRYPTO_SERVICE_AEAD:
320d0d859bbSFarhan Ali algo_mask = vcrypto->aead_algo;
321d0d859bbSFarhan Ali break;
32259ca6c93Szhenwei pi
32359ca6c93Szhenwei pi case VIRTIO_CRYPTO_SERVICE_AKCIPHER:
32459ca6c93Szhenwei pi algo_mask = vcrypto->akcipher_algo;
32559ca6c93Szhenwei pi break;
326d0d859bbSFarhan Ali }
327d0d859bbSFarhan Ali
328d0d859bbSFarhan Ali if (!(algo_mask & (1u << algo)))
329d0d859bbSFarhan Ali return false;
330d0d859bbSFarhan Ali
331d0d859bbSFarhan Ali return true;
332dbaf0624SGonglei }
333