162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci /* Management for virtio crypto devices (refer to adf_dev_mgr.c) 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2016 HUAWEI TECHNOLOGIES CO., LTD. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/mutex.h> 862306a36Sopenharmony_ci#include <linux/list.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <uapi/linux/virtio_crypto.h> 1262306a36Sopenharmony_ci#include "virtio_crypto_common.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic LIST_HEAD(virtio_crypto_table); 1562306a36Sopenharmony_cistatic uint32_t num_devices; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* The table_lock protects the above global list and num_devices */ 1862306a36Sopenharmony_cistatic DEFINE_MUTEX(table_lock); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define VIRTIO_CRYPTO_MAX_DEVICES 32 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * virtcrypto_devmgr_add_dev() - Add vcrypto_dev to the acceleration 2562306a36Sopenharmony_ci * framework. 2662306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Function adds virtio crypto device to the global list. 2962306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Return: 0 on success, error code othewise. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ciint virtcrypto_devmgr_add_dev(struct virtio_crypto *vcrypto_dev) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct list_head *itr; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci mutex_lock(&table_lock); 3862306a36Sopenharmony_ci if (num_devices == VIRTIO_CRYPTO_MAX_DEVICES) { 3962306a36Sopenharmony_ci pr_info("virtio_crypto: only support up to %d devices\n", 4062306a36Sopenharmony_ci VIRTIO_CRYPTO_MAX_DEVICES); 4162306a36Sopenharmony_ci mutex_unlock(&table_lock); 4262306a36Sopenharmony_ci return -EFAULT; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci list_for_each(itr, &virtio_crypto_table) { 4662306a36Sopenharmony_ci struct virtio_crypto *ptr = 4762306a36Sopenharmony_ci list_entry(itr, struct virtio_crypto, list); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (ptr == vcrypto_dev) { 5062306a36Sopenharmony_ci mutex_unlock(&table_lock); 5162306a36Sopenharmony_ci return -EEXIST; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci atomic_set(&vcrypto_dev->ref_count, 0); 5562306a36Sopenharmony_ci list_add_tail(&vcrypto_dev->list, &virtio_crypto_table); 5662306a36Sopenharmony_ci vcrypto_dev->dev_id = num_devices++; 5762306a36Sopenharmony_ci mutex_unlock(&table_lock); 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct list_head *virtcrypto_devmgr_get_head(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return &virtio_crypto_table; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * virtcrypto_devmgr_rm_dev() - Remove vcrypto_dev from the acceleration 6862306a36Sopenharmony_ci * framework. 6962306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Function removes virtio crypto device from the acceleration framework. 7262306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Return: void 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_civoid virtcrypto_devmgr_rm_dev(struct virtio_crypto *vcrypto_dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci mutex_lock(&table_lock); 7962306a36Sopenharmony_ci list_del(&vcrypto_dev->list); 8062306a36Sopenharmony_ci num_devices--; 8162306a36Sopenharmony_ci mutex_unlock(&table_lock); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * virtcrypto_devmgr_get_first() 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Function returns the first virtio crypto device from the acceleration 8862306a36Sopenharmony_ci * framework. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Return: pointer to vcrypto_dev or NULL if not found. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistruct virtio_crypto *virtcrypto_devmgr_get_first(void) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct virtio_crypto *dev = NULL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci mutex_lock(&table_lock); 9962306a36Sopenharmony_ci if (!list_empty(&virtio_crypto_table)) 10062306a36Sopenharmony_ci dev = list_first_entry(&virtio_crypto_table, 10162306a36Sopenharmony_ci struct virtio_crypto, 10262306a36Sopenharmony_ci list); 10362306a36Sopenharmony_ci mutex_unlock(&table_lock); 10462306a36Sopenharmony_ci return dev; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * virtcrypto_dev_in_use() - Check whether vcrypto_dev is currently in use 10962306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Return: 1 when device is in use, 0 otherwise. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ciint virtcrypto_dev_in_use(struct virtio_crypto *vcrypto_dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci return atomic_read(&vcrypto_dev->ref_count) != 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * virtcrypto_dev_get() - Increment vcrypto_dev reference count 12262306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Increment the vcrypto_dev refcount and if this is the first time 12562306a36Sopenharmony_ci * incrementing it during this period the vcrypto_dev is in use, 12662306a36Sopenharmony_ci * increment the module refcount too. 12762306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Return: 0 when successful, EFAULT when fail to bump module refcount 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ciint virtcrypto_dev_get(struct virtio_crypto *vcrypto_dev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (atomic_add_return(1, &vcrypto_dev->ref_count) == 1) 13462306a36Sopenharmony_ci if (!try_module_get(vcrypto_dev->owner)) 13562306a36Sopenharmony_ci return -EFAULT; 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * virtcrypto_dev_put() - Decrement vcrypto_dev reference count 14162306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Decrement the vcrypto_dev refcount and if this is the last time 14462306a36Sopenharmony_ci * decrementing it during this period the vcrypto_dev is in use, 14562306a36Sopenharmony_ci * decrement the module refcount too. 14662306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Return: void 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_civoid virtcrypto_dev_put(struct virtio_crypto *vcrypto_dev) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (atomic_sub_return(1, &vcrypto_dev->ref_count) == 0) 15362306a36Sopenharmony_ci module_put(vcrypto_dev->owner); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * virtcrypto_dev_started() - Check whether device has started 15862306a36Sopenharmony_ci * @vcrypto_dev: Pointer to virtio crypto device. 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Return: 1 when the device has started, 0 otherwise 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ciint virtcrypto_dev_started(struct virtio_crypto *vcrypto_dev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return (vcrypto_dev->status & VIRTIO_CRYPTO_S_HW_READY); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * virtcrypto_get_dev_node() - Get vcrypto_dev on the node. 17162306a36Sopenharmony_ci * @node: Node id the driver works. 17262306a36Sopenharmony_ci * @service: Crypto service that needs to be supported by the 17362306a36Sopenharmony_ci * dev 17462306a36Sopenharmony_ci * @algo: The algorithm number that needs to be supported by the 17562306a36Sopenharmony_ci * dev 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Function returns the virtio crypto device used fewest on the node, 17862306a36Sopenharmony_ci * and supports the given crypto service and algorithm. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * Return: pointer to vcrypto_dev or NULL if not found. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistruct virtio_crypto *virtcrypto_get_dev_node(int node, uint32_t service, 18562306a36Sopenharmony_ci uint32_t algo) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct virtio_crypto *vcrypto_dev = NULL, *tmp_dev; 18862306a36Sopenharmony_ci unsigned long best = ~0; 18962306a36Sopenharmony_ci unsigned long ctr; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci mutex_lock(&table_lock); 19262306a36Sopenharmony_ci list_for_each_entry(tmp_dev, virtcrypto_devmgr_get_head(), list) { 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if ((node == dev_to_node(&tmp_dev->vdev->dev) || 19562306a36Sopenharmony_ci dev_to_node(&tmp_dev->vdev->dev) < 0) && 19662306a36Sopenharmony_ci virtcrypto_dev_started(tmp_dev) && 19762306a36Sopenharmony_ci virtcrypto_algo_is_supported(tmp_dev, service, algo)) { 19862306a36Sopenharmony_ci ctr = atomic_read(&tmp_dev->ref_count); 19962306a36Sopenharmony_ci if (best > ctr) { 20062306a36Sopenharmony_ci vcrypto_dev = tmp_dev; 20162306a36Sopenharmony_ci best = ctr; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!vcrypto_dev) { 20762306a36Sopenharmony_ci pr_info("virtio_crypto: Could not find a device on node %d\n", 20862306a36Sopenharmony_ci node); 20962306a36Sopenharmony_ci /* Get any started device */ 21062306a36Sopenharmony_ci list_for_each_entry(tmp_dev, 21162306a36Sopenharmony_ci virtcrypto_devmgr_get_head(), list) { 21262306a36Sopenharmony_ci if (virtcrypto_dev_started(tmp_dev) && 21362306a36Sopenharmony_ci virtcrypto_algo_is_supported(tmp_dev, 21462306a36Sopenharmony_ci service, algo)) { 21562306a36Sopenharmony_ci vcrypto_dev = tmp_dev; 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci mutex_unlock(&table_lock); 22162306a36Sopenharmony_ci if (!vcrypto_dev) 22262306a36Sopenharmony_ci return NULL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci virtcrypto_dev_get(vcrypto_dev); 22562306a36Sopenharmony_ci return vcrypto_dev; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* 22962306a36Sopenharmony_ci * virtcrypto_dev_start() - Start virtio crypto device 23062306a36Sopenharmony_ci * @vcrypto: Pointer to virtio crypto device. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * Function notifies all the registered services that the virtio crypto device 23362306a36Sopenharmony_ci * is ready to be used. 23462306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Return: 0 on success, EFAULT when fail to register algorithms 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ciint virtcrypto_dev_start(struct virtio_crypto *vcrypto) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (virtio_crypto_skcipher_algs_register(vcrypto)) { 24162306a36Sopenharmony_ci pr_err("virtio_crypto: Failed to register crypto skcipher algs\n"); 24262306a36Sopenharmony_ci return -EFAULT; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (virtio_crypto_akcipher_algs_register(vcrypto)) { 24662306a36Sopenharmony_ci pr_err("virtio_crypto: Failed to register crypto akcipher algs\n"); 24762306a36Sopenharmony_ci virtio_crypto_skcipher_algs_unregister(vcrypto); 24862306a36Sopenharmony_ci return -EFAULT; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * virtcrypto_dev_stop() - Stop virtio crypto device 25662306a36Sopenharmony_ci * @vcrypto: Pointer to virtio crypto device. 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * Function notifies all the registered services that the virtio crypto device 25962306a36Sopenharmony_ci * is ready to be used. 26062306a36Sopenharmony_ci * To be used by virtio crypto device specific drivers. 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Return: void 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_civoid virtcrypto_dev_stop(struct virtio_crypto *vcrypto) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci virtio_crypto_skcipher_algs_unregister(vcrypto); 26762306a36Sopenharmony_ci virtio_crypto_akcipher_algs_unregister(vcrypto); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * vcrypto_algo_is_supported() 27262306a36Sopenharmony_ci * @vcrypto: Pointer to virtio crypto device. 27362306a36Sopenharmony_ci * @service: The bit number for service validate. 27462306a36Sopenharmony_ci * See VIRTIO_CRYPTO_SERVICE_* 27562306a36Sopenharmony_ci * @algo : The bit number for the algorithm to validate. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * Validate if the virtio crypto device supports a service and 27962306a36Sopenharmony_ci * algo. 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Return true if device supports a service and algo. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cibool virtcrypto_algo_is_supported(struct virtio_crypto *vcrypto, 28562306a36Sopenharmony_ci uint32_t service, 28662306a36Sopenharmony_ci uint32_t algo) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci uint32_t service_mask = 1u << service; 28962306a36Sopenharmony_ci uint32_t algo_mask = 0; 29062306a36Sopenharmony_ci bool low = true; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (algo > 31) { 29362306a36Sopenharmony_ci algo -= 32; 29462306a36Sopenharmony_ci low = false; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!(vcrypto->crypto_services & service_mask)) 29862306a36Sopenharmony_ci return false; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci switch (service) { 30162306a36Sopenharmony_ci case VIRTIO_CRYPTO_SERVICE_CIPHER: 30262306a36Sopenharmony_ci if (low) 30362306a36Sopenharmony_ci algo_mask = vcrypto->cipher_algo_l; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci algo_mask = vcrypto->cipher_algo_h; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci case VIRTIO_CRYPTO_SERVICE_HASH: 30962306a36Sopenharmony_ci algo_mask = vcrypto->hash_algo; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci case VIRTIO_CRYPTO_SERVICE_MAC: 31362306a36Sopenharmony_ci if (low) 31462306a36Sopenharmony_ci algo_mask = vcrypto->mac_algo_l; 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci algo_mask = vcrypto->mac_algo_h; 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci case VIRTIO_CRYPTO_SERVICE_AEAD: 32062306a36Sopenharmony_ci algo_mask = vcrypto->aead_algo; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci case VIRTIO_CRYPTO_SERVICE_AKCIPHER: 32462306a36Sopenharmony_ci algo_mask = vcrypto->akcipher_algo; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!(algo_mask & (1u << algo))) 32962306a36Sopenharmony_ci return false; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return true; 33262306a36Sopenharmony_ci} 333