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