1 /* 2 * QEMU Crypto Device Implementation 3 * 4 * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. 5 * 6 * Authors: 7 * Gonglei <arei.gonglei@huawei.com> 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 21 * 22 */ 23 24 #include "qemu/osdep.h" 25 #include "sysemu/cryptodev.h" 26 #include "qapi/error.h" 27 #include "qapi/qapi-commands-cryptodev.h" 28 #include "qapi/visitor.h" 29 #include "qemu/config-file.h" 30 #include "qemu/error-report.h" 31 #include "qemu/main-loop.h" 32 #include "qom/object_interfaces.h" 33 #include "hw/virtio/virtio-crypto.h" 34 35 36 static QTAILQ_HEAD(, CryptoDevBackendClient) crypto_clients; 37 38 static int qmp_query_cryptodev_foreach(Object *obj, void *data) 39 { 40 CryptoDevBackend *backend; 41 QCryptodevInfoList **infolist = data; 42 uint32_t services, i; 43 44 if (!object_dynamic_cast(obj, TYPE_CRYPTODEV_BACKEND)) { 45 return 0; 46 } 47 48 QCryptodevInfo *info = g_new0(QCryptodevInfo, 1); 49 info->id = g_strdup(object_get_canonical_path_component(obj)); 50 51 backend = CRYPTODEV_BACKEND(obj); 52 services = backend->conf.crypto_services; 53 for (i = 0; i < QCRYPTODEV_BACKEND_SERVICE__MAX; i++) { 54 if (services & (1 << i)) { 55 QAPI_LIST_PREPEND(info->service, i); 56 } 57 } 58 59 for (i = 0; i < backend->conf.peers.queues; i++) { 60 CryptoDevBackendClient *cc = backend->conf.peers.ccs[i]; 61 QCryptodevBackendClient *client = g_new0(QCryptodevBackendClient, 1); 62 63 client->queue = cc->queue_index; 64 client->type = cc->type; 65 QAPI_LIST_PREPEND(info->client, client); 66 } 67 68 QAPI_LIST_PREPEND(*infolist, info); 69 70 return 0; 71 } 72 73 QCryptodevInfoList *qmp_query_cryptodev(Error **errp) 74 { 75 QCryptodevInfoList *list = NULL; 76 Object *objs = container_get(object_get_root(), "/objects"); 77 78 object_child_foreach(objs, qmp_query_cryptodev_foreach, &list); 79 80 return list; 81 } 82 83 CryptoDevBackendClient *cryptodev_backend_new_client(void) 84 { 85 CryptoDevBackendClient *cc; 86 87 cc = g_new0(CryptoDevBackendClient, 1); 88 QTAILQ_INSERT_TAIL(&crypto_clients, cc, next); 89 90 return cc; 91 } 92 93 void cryptodev_backend_free_client( 94 CryptoDevBackendClient *cc) 95 { 96 QTAILQ_REMOVE(&crypto_clients, cc, next); 97 g_free(cc->info_str); 98 g_free(cc); 99 } 100 101 void cryptodev_backend_cleanup( 102 CryptoDevBackend *backend, 103 Error **errp) 104 { 105 CryptoDevBackendClass *bc = 106 CRYPTODEV_BACKEND_GET_CLASS(backend); 107 108 if (bc->cleanup) { 109 bc->cleanup(backend, errp); 110 } 111 112 g_free(backend->sym_stat); 113 g_free(backend->asym_stat); 114 } 115 116 int cryptodev_backend_create_session( 117 CryptoDevBackend *backend, 118 CryptoDevBackendSessionInfo *sess_info, 119 uint32_t queue_index, 120 CryptoDevCompletionFunc cb, 121 void *opaque) 122 { 123 CryptoDevBackendClass *bc = 124 CRYPTODEV_BACKEND_GET_CLASS(backend); 125 126 if (bc->create_session) { 127 return bc->create_session(backend, sess_info, queue_index, cb, opaque); 128 } 129 return -VIRTIO_CRYPTO_NOTSUPP; 130 } 131 132 int cryptodev_backend_close_session( 133 CryptoDevBackend *backend, 134 uint64_t session_id, 135 uint32_t queue_index, 136 CryptoDevCompletionFunc cb, 137 void *opaque) 138 { 139 CryptoDevBackendClass *bc = 140 CRYPTODEV_BACKEND_GET_CLASS(backend); 141 142 if (bc->close_session) { 143 return bc->close_session(backend, session_id, queue_index, cb, opaque); 144 } 145 return -VIRTIO_CRYPTO_NOTSUPP; 146 } 147 148 static int cryptodev_backend_operation( 149 CryptoDevBackend *backend, 150 CryptoDevBackendOpInfo *op_info) 151 { 152 CryptoDevBackendClass *bc = 153 CRYPTODEV_BACKEND_GET_CLASS(backend); 154 155 if (bc->do_op) { 156 return bc->do_op(backend, op_info); 157 } 158 return -VIRTIO_CRYPTO_NOTSUPP; 159 } 160 161 static int cryptodev_backend_account(CryptoDevBackend *backend, 162 CryptoDevBackendOpInfo *op_info) 163 { 164 enum QCryptodevBackendAlgType algtype = op_info->algtype; 165 int len; 166 167 if (algtype == QCRYPTODEV_BACKEND_ALG_ASYM) { 168 CryptoDevBackendAsymOpInfo *asym_op_info = op_info->u.asym_op_info; 169 len = asym_op_info->src_len; 170 switch (op_info->op_code) { 171 case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: 172 CryptodevAsymStatIncEncrypt(backend, len); 173 break; 174 case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: 175 CryptodevAsymStatIncDecrypt(backend, len); 176 break; 177 case VIRTIO_CRYPTO_AKCIPHER_SIGN: 178 CryptodevAsymStatIncSign(backend, len); 179 break; 180 case VIRTIO_CRYPTO_AKCIPHER_VERIFY: 181 CryptodevAsymStatIncVerify(backend, len); 182 break; 183 default: 184 return -VIRTIO_CRYPTO_NOTSUPP; 185 } 186 } else if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) { 187 CryptoDevBackendSymOpInfo *sym_op_info = op_info->u.sym_op_info; 188 len = sym_op_info->src_len; 189 switch (op_info->op_code) { 190 case VIRTIO_CRYPTO_CIPHER_ENCRYPT: 191 CryptodevSymStatIncEncrypt(backend, len); 192 break; 193 case VIRTIO_CRYPTO_CIPHER_DECRYPT: 194 CryptodevSymStatIncDecrypt(backend, len); 195 break; 196 default: 197 return -VIRTIO_CRYPTO_NOTSUPP; 198 } 199 } else { 200 error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype); 201 return -VIRTIO_CRYPTO_NOTSUPP; 202 } 203 204 return len; 205 } 206 207 static void cryptodev_backend_throttle_timer_cb(void *opaque) 208 { 209 CryptoDevBackend *backend = (CryptoDevBackend *)opaque; 210 CryptoDevBackendOpInfo *op_info, *tmpop; 211 int ret; 212 213 QTAILQ_FOREACH_SAFE(op_info, &backend->opinfos, next, tmpop) { 214 QTAILQ_REMOVE(&backend->opinfos, op_info, next); 215 ret = cryptodev_backend_account(backend, op_info); 216 if (ret < 0) { 217 op_info->cb(op_info->opaque, ret); 218 continue; 219 } 220 221 throttle_account(&backend->ts, true, ret); 222 cryptodev_backend_operation(backend, op_info); 223 if (throttle_enabled(&backend->tc) && 224 throttle_schedule_timer(&backend->ts, &backend->tt, true)) { 225 break; 226 } 227 } 228 } 229 230 int cryptodev_backend_crypto_operation( 231 CryptoDevBackend *backend, 232 CryptoDevBackendOpInfo *op_info) 233 { 234 int ret; 235 236 if (!throttle_enabled(&backend->tc)) { 237 goto do_account; 238 } 239 240 if (throttle_schedule_timer(&backend->ts, &backend->tt, true) || 241 !QTAILQ_EMPTY(&backend->opinfos)) { 242 QTAILQ_INSERT_TAIL(&backend->opinfos, op_info, next); 243 return 0; 244 } 245 246 do_account: 247 ret = cryptodev_backend_account(backend, op_info); 248 if (ret < 0) { 249 return ret; 250 } 251 252 throttle_account(&backend->ts, true, ret); 253 254 return cryptodev_backend_operation(backend, op_info); 255 } 256 257 static void 258 cryptodev_backend_get_queues(Object *obj, Visitor *v, const char *name, 259 void *opaque, Error **errp) 260 { 261 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 262 uint32_t value = backend->conf.peers.queues; 263 264 visit_type_uint32(v, name, &value, errp); 265 } 266 267 static void 268 cryptodev_backend_set_queues(Object *obj, Visitor *v, const char *name, 269 void *opaque, Error **errp) 270 { 271 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 272 uint32_t value; 273 274 if (!visit_type_uint32(v, name, &value, errp)) { 275 return; 276 } 277 if (!value) { 278 error_setg(errp, "Property '%s.%s' doesn't take value '%" PRIu32 "'", 279 object_get_typename(obj), name, value); 280 return; 281 } 282 backend->conf.peers.queues = value; 283 } 284 285 static void cryptodev_backend_set_throttle(CryptoDevBackend *backend, int field, 286 uint64_t value, Error **errp) 287 { 288 uint64_t orig = backend->tc.buckets[field].avg; 289 bool enabled = throttle_enabled(&backend->tc); 290 291 if (orig == value) { 292 return; 293 } 294 295 backend->tc.buckets[field].avg = value; 296 if (!throttle_enabled(&backend->tc)) { 297 throttle_timers_destroy(&backend->tt); 298 cryptodev_backend_throttle_timer_cb(backend); /* drain opinfos */ 299 return; 300 } 301 302 if (!throttle_is_valid(&backend->tc, errp)) { 303 backend->tc.buckets[field].avg = orig; /* revert change */ 304 return; 305 } 306 307 if (!enabled) { 308 throttle_init(&backend->ts); 309 throttle_timers_init(&backend->tt, qemu_get_aio_context(), 310 QEMU_CLOCK_REALTIME, 311 cryptodev_backend_throttle_timer_cb, /* FIXME */ 312 cryptodev_backend_throttle_timer_cb, backend); 313 } 314 315 throttle_config(&backend->ts, QEMU_CLOCK_REALTIME, &backend->tc); 316 } 317 318 static void cryptodev_backend_get_bps(Object *obj, Visitor *v, 319 const char *name, void *opaque, 320 Error **errp) 321 { 322 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 323 uint64_t value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; 324 325 visit_type_uint64(v, name, &value, errp); 326 } 327 328 static void cryptodev_backend_set_bps(Object *obj, Visitor *v, const char *name, 329 void *opaque, Error **errp) 330 { 331 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 332 uint64_t value; 333 334 if (!visit_type_uint64(v, name, &value, errp)) { 335 return; 336 } 337 338 cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); 339 } 340 341 static void cryptodev_backend_get_ops(Object *obj, Visitor *v, const char *name, 342 void *opaque, Error **errp) 343 { 344 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 345 uint64_t value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; 346 347 visit_type_uint64(v, name, &value, errp); 348 } 349 350 static void cryptodev_backend_set_ops(Object *obj, Visitor *v, 351 const char *name, void *opaque, 352 Error **errp) 353 { 354 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 355 uint64_t value; 356 357 if (!visit_type_uint64(v, name, &value, errp)) { 358 return; 359 } 360 361 cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); 362 } 363 364 static void 365 cryptodev_backend_complete(UserCreatable *uc, Error **errp) 366 { 367 CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); 368 CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); 369 uint32_t services; 370 uint64_t value; 371 372 QTAILQ_INIT(&backend->opinfos); 373 value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; 374 cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); 375 value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; 376 cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); 377 378 if (bc->init) { 379 bc->init(backend, errp); 380 } 381 382 services = backend->conf.crypto_services; 383 if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_CIPHER)) { 384 backend->sym_stat = g_new0(CryptodevBackendSymStat, 1); 385 } 386 387 if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER)) { 388 backend->asym_stat = g_new0(CryptodevBackendAsymStat, 1); 389 } 390 } 391 392 void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used) 393 { 394 backend->is_used = used; 395 } 396 397 bool cryptodev_backend_is_used(CryptoDevBackend *backend) 398 { 399 return backend->is_used; 400 } 401 402 void cryptodev_backend_set_ready(CryptoDevBackend *backend, bool ready) 403 { 404 backend->ready = ready; 405 } 406 407 bool cryptodev_backend_is_ready(CryptoDevBackend *backend) 408 { 409 return backend->ready; 410 } 411 412 static bool 413 cryptodev_backend_can_be_deleted(UserCreatable *uc) 414 { 415 return !cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc)); 416 } 417 418 static void cryptodev_backend_instance_init(Object *obj) 419 { 420 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 421 422 /* Initialize devices' queues property to 1 */ 423 object_property_set_int(obj, "queues", 1, NULL); 424 425 throttle_config_init(&backend->tc); 426 } 427 428 static void cryptodev_backend_finalize(Object *obj) 429 { 430 CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); 431 432 cryptodev_backend_cleanup(backend, NULL); 433 if (throttle_enabled(&backend->tc)) { 434 throttle_timers_destroy(&backend->tt); 435 } 436 } 437 438 static void 439 cryptodev_backend_class_init(ObjectClass *oc, void *data) 440 { 441 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 442 443 ucc->complete = cryptodev_backend_complete; 444 ucc->can_be_deleted = cryptodev_backend_can_be_deleted; 445 446 QTAILQ_INIT(&crypto_clients); 447 object_class_property_add(oc, "queues", "uint32", 448 cryptodev_backend_get_queues, 449 cryptodev_backend_set_queues, 450 NULL, NULL); 451 object_class_property_add(oc, "throttle-bps", "uint64", 452 cryptodev_backend_get_bps, 453 cryptodev_backend_set_bps, 454 NULL, NULL); 455 object_class_property_add(oc, "throttle-ops", "uint64", 456 cryptodev_backend_get_ops, 457 cryptodev_backend_set_ops, 458 NULL, NULL); 459 } 460 461 static const TypeInfo cryptodev_backend_info = { 462 .name = TYPE_CRYPTODEV_BACKEND, 463 .parent = TYPE_OBJECT, 464 .instance_size = sizeof(CryptoDevBackend), 465 .instance_init = cryptodev_backend_instance_init, 466 .instance_finalize = cryptodev_backend_finalize, 467 .class_size = sizeof(CryptoDevBackendClass), 468 .class_init = cryptodev_backend_class_init, 469 .interfaces = (InterfaceInfo[]) { 470 { TYPE_USER_CREATABLE }, 471 { } 472 } 473 }; 474 475 static void 476 cryptodev_backend_register_types(void) 477 { 478 type_register_static(&cryptodev_backend_info); 479 } 480 481 type_init(cryptodev_backend_register_types); 482