| /* | |
| * QEMU Cryptodev backend for QEMU cipher APIs | |
| * | |
| * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. | |
| * | |
| * Authors: | |
| * Gonglei <arei.gonglei@huawei.com> | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
| * | |
| */ | |
| #include "qemu/osdep.h" | |
| #include "system/cryptodev.h" | |
| #include "qemu/error-report.h" | |
| #include "qapi/error.h" | |
| #include "standard-headers/linux/virtio_crypto.h" | |
| #include "crypto/cipher.h" | |
| #include "crypto/akcipher.h" | |
| #include "qom/object.h" | |
| /** | |
| * @TYPE_CRYPTODEV_BACKEND_BUILTIN: | |
| * name of backend that uses QEMU cipher API | |
| */ | |
| #define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin" | |
| OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN) | |
| typedef struct CryptoDevBackendBuiltinSession { | |
| QCryptoCipher *cipher; | |
| uint8_t direction; /* encryption or decryption */ | |
| uint8_t type; /* cipher? hash? aead? */ | |
| QCryptoAkCipher *akcipher; | |
| QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next; | |
| } CryptoDevBackendBuiltinSession; | |
| /* Max number of symmetric/asymmetric sessions */ | |
| #define MAX_NUM_SESSIONS 256 | |
| #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512 | |
| #define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64 | |
| struct CryptoDevBackendBuiltin { | |
| CryptoDevBackend parent_obj; | |
| CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; | |
| }; | |
| static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend) | |
| { | |
| QCryptoAkCipherOptions opts; | |
| opts.alg = QCRYPTO_AK_CIPHER_ALGO_RSA; | |
| opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW; | |
| if (qcrypto_akcipher_supports(&opts)) { | |
| backend->conf.crypto_services |= | |
| (1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER); | |
| backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; | |
| } | |
| } | |
| static void cryptodev_builtin_init( | |
| CryptoDevBackend *backend, Error **errp) | |
| { | |
| /* Only support one queue */ | |
| int queues = backend->conf.peers.queues; | |
| CryptoDevBackendClient *cc; | |
| if (queues != 1) { | |
| error_setg(errp, | |
| "Only support one queue in cryptdov-builtin backend"); | |
| return; | |
| } | |
| cc = cryptodev_backend_new_client(); | |
| cc->info_str = g_strdup_printf("cryptodev-builtin0"); | |
| cc->queue_index = 0; | |
| cc->type = QCRYPTODEV_BACKEND_TYPE_BUILTIN; | |
| backend->conf.peers.ccs[0] = cc; | |
| backend->conf.crypto_services = | |
| 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_CIPHER | | |
| 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH | | |
| 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC; | |
| backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; | |
| backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; | |
| /* | |
| * Set the Maximum length of crypto request. | |
| * Why this value? Just avoid to overflow when | |
| * memory allocation for each crypto request. | |
| */ | |
| backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo); | |
| backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN; | |
| backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN; | |
| cryptodev_builtin_init_akcipher(backend); | |
| cryptodev_backend_set_ready(backend, true); | |
| } | |
| static int | |
| cryptodev_builtin_get_unused_session_index( | |
| CryptoDevBackendBuiltin *builtin) | |
| { | |
| size_t i; | |
| for (i = 0; i < MAX_NUM_SESSIONS; i++) { | |
| if (builtin->sessions[i] == NULL) { | |
| return i; | |
| } | |
| } | |
| return -1; | |
| } | |
| #define AES_KEYSIZE_128 16 | |
| #define AES_KEYSIZE_192 24 | |
| #define AES_KEYSIZE_256 32 | |
| #define AES_KEYSIZE_128_XTS AES_KEYSIZE_256 | |
| #define AES_KEYSIZE_256_XTS 64 | |
| static int | |
| cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp) | |
| { | |
| int algo; | |
| if (key_len == AES_KEYSIZE_128) { | |
| algo = QCRYPTO_CIPHER_ALGO_AES_128; | |
| } else if (key_len == AES_KEYSIZE_192) { | |
| algo = QCRYPTO_CIPHER_ALGO_AES_192; | |
| } else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */ | |
| if (mode == QCRYPTO_CIPHER_MODE_XTS) { | |
| algo = QCRYPTO_CIPHER_ALGO_AES_128; | |
| } else { | |
| algo = QCRYPTO_CIPHER_ALGO_AES_256; | |
| } | |
| } else if (key_len == AES_KEYSIZE_256_XTS) { | |
| if (mode == QCRYPTO_CIPHER_MODE_XTS) { | |
| algo = QCRYPTO_CIPHER_ALGO_AES_256; | |
| } else { | |
| goto err; | |
| } | |
| } else { | |
| goto err; | |
| } | |
| return algo; | |
| err: | |
| error_setg(errp, "Unsupported key length :%u", key_len); | |
| return -1; | |
| } | |
| static int cryptodev_builtin_get_rsa_hash_algo( | |
| int virtio_rsa_hash, Error **errp) | |
| { | |
| switch (virtio_rsa_hash) { | |
| case VIRTIO_CRYPTO_RSA_MD5: | |
| return QCRYPTO_HASH_ALGO_MD5; | |
| case VIRTIO_CRYPTO_RSA_SHA1: | |
| return QCRYPTO_HASH_ALGO_SHA1; | |
| case VIRTIO_CRYPTO_RSA_SHA256: | |
| return QCRYPTO_HASH_ALGO_SHA256; | |
| case VIRTIO_CRYPTO_RSA_SHA512: | |
| return QCRYPTO_HASH_ALGO_SHA512; | |
| default: | |
| error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash); | |
| return -1; | |
| } | |
| } | |
| static int cryptodev_builtin_set_rsa_options( | |
| int virtio_padding_algo, | |
| int virtio_hash_algo, | |
| QCryptoAkCipherOptionsRSA *opt, | |
| Error **errp) | |
| { | |
| if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) { | |
| int hash_alg; | |
| hash_alg = cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp); | |
| if (hash_alg < 0) { | |
| return -1; | |
| } | |
| opt->hash_alg = hash_alg; | |
| opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1; | |
| return 0; | |
| } | |
| if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) { | |
| opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW; | |
| return 0; | |
| } | |
| error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo); | |
| return -1; | |
| } | |
| static int cryptodev_builtin_create_cipher_session( | |
| CryptoDevBackendBuiltin *builtin, | |
| CryptoDevBackendSymSessionInfo *sess_info, | |
| Error **errp) | |
| { | |
| int algo; | |
| int mode; | |
| QCryptoCipher *cipher; | |
| int index; | |
| CryptoDevBackendBuiltinSession *sess; | |
| if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) { | |
| error_setg(errp, "Unsupported optype :%u", sess_info->op_type); | |
| return -1; | |
| } | |
| index = cryptodev_builtin_get_unused_session_index(builtin); | |
| if (index < 0) { | |
| error_setg(errp, "Total number of sessions created exceeds %u", | |
| MAX_NUM_SESSIONS); | |
| return -1; | |
| } | |
| switch (sess_info->cipher_alg) { | |
| case VIRTIO_CRYPTO_CIPHER_AES_ECB: | |
| mode = QCRYPTO_CIPHER_MODE_ECB; | |
| algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, | |
| mode, errp); | |
| if (algo < 0) { | |
| return -1; | |
| } | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_AES_CBC: | |
| mode = QCRYPTO_CIPHER_MODE_CBC; | |
| algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, | |
| mode, errp); | |
| if (algo < 0) { | |
| return -1; | |
| } | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_AES_CTR: | |
| mode = QCRYPTO_CIPHER_MODE_CTR; | |
| algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, | |
| mode, errp); | |
| if (algo < 0) { | |
| return -1; | |
| } | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_AES_XTS: | |
| mode = QCRYPTO_CIPHER_MODE_XTS; | |
| algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, | |
| mode, errp); | |
| if (algo < 0) { | |
| return -1; | |
| } | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_3DES_ECB: | |
| mode = QCRYPTO_CIPHER_MODE_ECB; | |
| algo = QCRYPTO_CIPHER_ALGO_3DES; | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_3DES_CBC: | |
| mode = QCRYPTO_CIPHER_MODE_CBC; | |
| algo = QCRYPTO_CIPHER_ALGO_3DES; | |
| break; | |
| case VIRTIO_CRYPTO_CIPHER_3DES_CTR: | |
| mode = QCRYPTO_CIPHER_MODE_CTR; | |
| algo = QCRYPTO_CIPHER_ALGO_3DES; | |
| break; | |
| default: | |
| error_setg(errp, "Unsupported cipher alg :%u", | |
| sess_info->cipher_alg); | |
| return -1; | |
| } | |
| cipher = qcrypto_cipher_new(algo, mode, | |
| sess_info->cipher_key, | |
| sess_info->key_len, | |
| errp); | |
| if (!cipher) { | |
| return -1; | |
| } | |
| sess = g_new0(CryptoDevBackendBuiltinSession, 1); | |
| sess->cipher = cipher; | |
| sess->direction = sess_info->direction; | |
| sess->type = sess_info->op_type; | |
| builtin->sessions[index] = sess; | |
| return index; | |
| } | |
| static int cryptodev_builtin_create_akcipher_session( | |
| CryptoDevBackendBuiltin *builtin, | |
| CryptoDevBackendAsymSessionInfo *sess_info, | |
| Error **errp) | |
| { | |
| CryptoDevBackendBuiltinSession *sess; | |
| QCryptoAkCipher *akcipher; | |
| int index; | |
| QCryptoAkCipherKeyType type; | |
| QCryptoAkCipherOptions opts; | |
| switch (sess_info->algo) { | |
| case VIRTIO_CRYPTO_AKCIPHER_RSA: | |
| opts.alg = QCRYPTO_AK_CIPHER_ALGO_RSA; | |
| if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo, | |
| sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) { | |
| return -1; | |
| } | |
| break; | |
| /* TODO support DSA&ECDSA until qemu crypto framework support these */ | |
| default: | |
| error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo); | |
| return -1; | |
| } | |
| switch (sess_info->keytype) { | |
| case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: | |
| type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC; | |
| break; | |
| case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: | |
| type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE; | |
| break; | |
| default: | |
| error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype); | |
| return -1; | |
| } | |
| index = cryptodev_builtin_get_unused_session_index(builtin); | |
| if (index < 0) { | |
| error_setg(errp, "Total number of sessions created exceeds %u", | |
| MAX_NUM_SESSIONS); | |
| return -1; | |
| } | |
| akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key, | |
| sess_info->keylen, errp); | |
| if (!akcipher) { | |
| return -1; | |
| } | |
| sess = g_new0(CryptoDevBackendBuiltinSession, 1); | |
| sess->akcipher = akcipher; | |
| builtin->sessions[index] = sess; | |
| return index; | |
| } | |
| static int cryptodev_builtin_create_session( | |
| CryptoDevBackend *backend, | |
| CryptoDevBackendSessionInfo *sess_info, | |
| uint32_t queue_index, | |
| CryptoDevCompletionFunc cb, | |
| void *opaque) | |
| { | |
| CryptoDevBackendBuiltin *builtin = | |
| CRYPTODEV_BACKEND_BUILTIN(backend); | |
| CryptoDevBackendSymSessionInfo *sym_sess_info; | |
| CryptoDevBackendAsymSessionInfo *asym_sess_info; | |
| int ret, status; | |
| Error *local_error = NULL; | |
| switch (sess_info->op_code) { | |
| case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: | |
| sym_sess_info = &sess_info->u.sym_sess_info; | |
| ret = cryptodev_builtin_create_cipher_session( | |
| builtin, sym_sess_info, &local_error); | |
| break; | |
| case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: | |
| asym_sess_info = &sess_info->u.asym_sess_info; | |
| ret = cryptodev_builtin_create_akcipher_session( | |
| builtin, asym_sess_info, &local_error); | |
| break; | |
| case VIRTIO_CRYPTO_HASH_CREATE_SESSION: | |
| case VIRTIO_CRYPTO_MAC_CREATE_SESSION: | |
| default: | |
| error_report("Unsupported opcode :%" PRIu32 "", | |
| sess_info->op_code); | |
| return -VIRTIO_CRYPTO_NOTSUPP; | |
| } | |
| if (local_error) { | |
| error_report_err(local_error); | |
| } | |
| if (ret < 0) { | |
| status = -VIRTIO_CRYPTO_ERR; | |
| } else { | |
| sess_info->session_id = ret; | |
| status = VIRTIO_CRYPTO_OK; | |
| } | |
| if (cb) { | |
| cb(opaque, status); | |
| } | |
| return 0; | |
| } | |
| static int cryptodev_builtin_close_session( | |
| CryptoDevBackend *backend, | |
| uint64_t session_id, | |
| uint32_t queue_index, | |
| CryptoDevCompletionFunc cb, | |
| void *opaque) | |
| { | |
| CryptoDevBackendBuiltin *builtin = | |
| CRYPTODEV_BACKEND_BUILTIN(backend); | |
| CryptoDevBackendBuiltinSession *session; | |
| if (session_id >= MAX_NUM_SESSIONS || !builtin->sessions[session_id]) { | |
| return -VIRTIO_CRYPTO_INVSESS; | |
| } | |
| session = builtin->sessions[session_id]; | |
| if (session->cipher) { | |
| qcrypto_cipher_free(session->cipher); | |
| } else if (session->akcipher) { | |
| qcrypto_akcipher_free(session->akcipher); | |
| } | |
| g_free(session); | |
| builtin->sessions[session_id] = NULL; | |
| if (cb) { | |
| cb(opaque, VIRTIO_CRYPTO_OK); | |
| } | |
| return 0; | |
| } | |
| static int cryptodev_builtin_sym_operation( | |
| CryptoDevBackendBuiltinSession *sess, | |
| CryptoDevBackendSymOpInfo *op_info, Error **errp) | |
| { | |
| int ret; | |
| if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { | |
| error_setg(errp, | |
| "Algorithm chain is unsupported for cryptdoev-builtin"); | |
| return -VIRTIO_CRYPTO_NOTSUPP; | |
| } | |
| if (op_info->iv_len > 0) { | |
| ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, | |
| op_info->iv_len, errp); | |
| if (ret < 0) { | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| } | |
| if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) { | |
| ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src, | |
| op_info->dst, op_info->src_len, errp); | |
| if (ret < 0) { | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| } else { | |
| ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src, | |
| op_info->dst, op_info->src_len, errp); | |
| if (ret < 0) { | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| } | |
| return VIRTIO_CRYPTO_OK; | |
| } | |
| static int cryptodev_builtin_asym_operation( | |
| CryptoDevBackendBuiltinSession *sess, uint32_t op_code, | |
| CryptoDevBackendAsymOpInfo *op_info, Error **errp) | |
| { | |
| int ret; | |
| switch (op_code) { | |
| case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: | |
| ret = qcrypto_akcipher_encrypt(sess->akcipher, | |
| op_info->src, op_info->src_len, | |
| op_info->dst, op_info->dst_len, errp); | |
| break; | |
| case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: | |
| ret = qcrypto_akcipher_decrypt(sess->akcipher, | |
| op_info->src, op_info->src_len, | |
| op_info->dst, op_info->dst_len, errp); | |
| break; | |
| case VIRTIO_CRYPTO_AKCIPHER_SIGN: | |
| ret = qcrypto_akcipher_sign(sess->akcipher, | |
| op_info->src, op_info->src_len, | |
| op_info->dst, op_info->dst_len, errp); | |
| break; | |
| case VIRTIO_CRYPTO_AKCIPHER_VERIFY: | |
| ret = qcrypto_akcipher_verify(sess->akcipher, | |
| op_info->src, op_info->src_len, | |
| op_info->dst, op_info->dst_len, errp); | |
| break; | |
| default: | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| if (ret < 0) { | |
| if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { | |
| return -VIRTIO_CRYPTO_KEY_REJECTED; | |
| } | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| /* Buffer is too short, typically the driver should handle this case */ | |
| if (unlikely(ret > op_info->dst_len)) { | |
| if (errp && !*errp) { | |
| error_setg(errp, "dst buffer too short"); | |
| } | |
| return -VIRTIO_CRYPTO_ERR; | |
| } | |
| op_info->dst_len = ret; | |
| return VIRTIO_CRYPTO_OK; | |
| } | |
| static int cryptodev_builtin_operation( | |
| CryptoDevBackend *backend, | |
| CryptoDevBackendOpInfo *op_info) | |
| { | |
| CryptoDevBackendBuiltin *builtin = | |
| CRYPTODEV_BACKEND_BUILTIN(backend); | |
| CryptoDevBackendBuiltinSession *sess; | |
| CryptoDevBackendSymOpInfo *sym_op_info; | |
| CryptoDevBackendAsymOpInfo *asym_op_info; | |
| QCryptodevBackendAlgoType algtype = op_info->algtype; | |
| int status = -VIRTIO_CRYPTO_ERR; | |
| Error *local_error = NULL; | |
| if (op_info->session_id >= MAX_NUM_SESSIONS || | |
| builtin->sessions[op_info->session_id] == NULL) { | |
| error_report("Cannot find a valid session id: %" PRIu64 "", | |
| op_info->session_id); | |
| return -VIRTIO_CRYPTO_INVSESS; | |
| } | |
| sess = builtin->sessions[op_info->session_id]; | |
| if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { | |
| sym_op_info = op_info->u.sym_op_info; | |
| status = cryptodev_builtin_sym_operation(sess, sym_op_info, | |
| &local_error); | |
| } else if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { | |
| asym_op_info = op_info->u.asym_op_info; | |
| status = cryptodev_builtin_asym_operation(sess, op_info->op_code, | |
| asym_op_info, &local_error); | |
| } | |
| if (local_error) { | |
| error_report_err(local_error); | |
| } | |
| if (op_info->cb) { | |
| op_info->cb(op_info->opaque, status); | |
| } | |
| return 0; | |
| } | |
| static void cryptodev_builtin_cleanup( | |
| CryptoDevBackend *backend, | |
| Error **errp) | |
| { | |
| CryptoDevBackendBuiltin *builtin = | |
| CRYPTODEV_BACKEND_BUILTIN(backend); | |
| size_t i; | |
| int queues = backend->conf.peers.queues; | |
| CryptoDevBackendClient *cc; | |
| for (i = 0; i < MAX_NUM_SESSIONS; i++) { | |
| if (builtin->sessions[i] != NULL) { | |
| cryptodev_builtin_close_session(backend, i, 0, NULL, NULL); | |
| } | |
| } | |
| for (i = 0; i < queues; i++) { | |
| cc = backend->conf.peers.ccs[i]; | |
| if (cc) { | |
| cryptodev_backend_free_client(cc); | |
| backend->conf.peers.ccs[i] = NULL; | |
| } | |
| } | |
| cryptodev_backend_set_ready(backend, false); | |
| } | |
| static void | |
| cryptodev_builtin_class_init(ObjectClass *oc, const void *data) | |
| { | |
| CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); | |
| bc->init = cryptodev_builtin_init; | |
| bc->cleanup = cryptodev_builtin_cleanup; | |
| bc->create_session = cryptodev_builtin_create_session; | |
| bc->close_session = cryptodev_builtin_close_session; | |
| bc->do_op = cryptodev_builtin_operation; | |
| } | |
| static const TypeInfo cryptodev_builtin_info = { | |
| .name = TYPE_CRYPTODEV_BACKEND_BUILTIN, | |
| .parent = TYPE_CRYPTODEV_BACKEND, | |
| .class_init = cryptodev_builtin_class_init, | |
| .instance_size = sizeof(CryptoDevBackendBuiltin), | |
| }; | |
| static void | |
| cryptodev_builtin_register_types(void) | |
| { | |
| type_register_static(&cryptodev_builtin_info); | |
| } | |
| type_init(cryptodev_builtin_register_types); |