1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * pkey cca specific code 4 * 5 * Copyright IBM Corp. 2024 6 */ 7 8 #define KMSG_COMPONENT "pkey" 9 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 10 11 #include <linux/init.h> 12 #include <linux/module.h> 13 #include <linux/cpufeature.h> 14 15 #include "zcrypt_ccamisc.h" 16 #include "pkey_base.h" 17 18 MODULE_LICENSE("GPL"); 19 MODULE_AUTHOR("IBM Corporation"); 20 MODULE_DESCRIPTION("s390 protected key CCA handler"); 21 22 #if IS_MODULE(CONFIG_PKEY_CCA) 23 static struct ap_device_id pkey_cca_card_ids[] = { 24 { .dev_type = AP_DEVICE_TYPE_CEX4 }, 25 { .dev_type = AP_DEVICE_TYPE_CEX5 }, 26 { .dev_type = AP_DEVICE_TYPE_CEX6 }, 27 { .dev_type = AP_DEVICE_TYPE_CEX7 }, 28 { .dev_type = AP_DEVICE_TYPE_CEX8 }, 29 { /* end of list */ }, 30 }; 31 MODULE_DEVICE_TABLE(ap, pkey_cca_card_ids); 32 #endif 33 34 /* 35 * Check key blob for known and supported CCA key. 36 */ 37 static bool is_cca_key(const u8 *key, u32 keylen) 38 { 39 struct keytoken_header *hdr = (struct keytoken_header *)key; 40 41 if (keylen < sizeof(*hdr)) 42 return false; 43 44 switch (hdr->type) { 45 case TOKTYPE_CCA_INTERNAL: 46 switch (hdr->version) { 47 case TOKVER_CCA_AES: 48 case TOKVER_CCA_VLSC: 49 return true; 50 default: 51 return false; 52 } 53 case TOKTYPE_CCA_INTERNAL_PKA: 54 return true; 55 default: 56 return false; 57 } 58 } 59 60 static bool is_cca_keytype(enum pkey_key_type key_type) 61 { 62 switch (key_type) { 63 case PKEY_TYPE_CCA_DATA: 64 case PKEY_TYPE_CCA_CIPHER: 65 case PKEY_TYPE_CCA_ECC: 66 return true; 67 default: 68 return false; 69 } 70 } 71 72 static int cca_apqns4key(const u8 *key, u32 keylen, u32 flags, 73 struct pkey_apqn *apqns, size_t *nr_apqns) 74 { 75 struct keytoken_header *hdr = (struct keytoken_header *)key; 76 u32 _nr_apqns, *_apqns = NULL; 77 int rc; 78 79 if (!flags) 80 flags = PKEY_FLAGS_MATCH_CUR_MKVP | PKEY_FLAGS_MATCH_ALT_MKVP; 81 82 if (keylen < sizeof(struct keytoken_header)) 83 return -EINVAL; 84 85 zcrypt_wait_api_operational(); 86 87 if (hdr->type == TOKTYPE_CCA_INTERNAL) { 88 u64 cur_mkvp = 0, old_mkvp = 0; 89 int minhwtype = ZCRYPT_CEX3C; 90 91 if (hdr->version == TOKVER_CCA_AES) { 92 struct secaeskeytoken *t = (struct secaeskeytoken *)key; 93 94 if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) 95 cur_mkvp = t->mkvp; 96 if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) 97 old_mkvp = t->mkvp; 98 } else if (hdr->version == TOKVER_CCA_VLSC) { 99 struct cipherkeytoken *t = (struct cipherkeytoken *)key; 100 101 minhwtype = ZCRYPT_CEX6; 102 if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) 103 cur_mkvp = t->mkvp0; 104 if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) 105 old_mkvp = t->mkvp0; 106 } else { 107 /* unknown CCA internal token type */ 108 return -EINVAL; 109 } 110 rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, 111 minhwtype, AES_MK_SET, 112 cur_mkvp, old_mkvp, 1); 113 if (rc) 114 goto out; 115 116 } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { 117 struct eccprivkeytoken *t = (struct eccprivkeytoken *)key; 118 u64 cur_mkvp = 0, old_mkvp = 0; 119 120 if (t->secid == 0x20) { 121 if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) 122 cur_mkvp = t->mkvp; 123 if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) 124 old_mkvp = t->mkvp; 125 } else { 126 /* unknown CCA internal 2 token type */ 127 return -EINVAL; 128 } 129 rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, 130 ZCRYPT_CEX7, APKA_MK_SET, 131 cur_mkvp, old_mkvp, 1); 132 if (rc) 133 goto out; 134 135 } else { 136 PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n", 137 __func__, hdr->type, hdr->version); 138 return -EINVAL; 139 } 140 141 if (apqns) { 142 if (*nr_apqns < _nr_apqns) 143 rc = -ENOSPC; 144 else 145 memcpy(apqns, _apqns, _nr_apqns * sizeof(u32)); 146 } 147 *nr_apqns = _nr_apqns; 148 149 out: 150 kfree(_apqns); 151 pr_debug("rc=%d\n", rc); 152 return rc; 153 } 154 155 static int cca_apqns4type(enum pkey_key_type ktype, 156 u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags, 157 struct pkey_apqn *apqns, size_t *nr_apqns) 158 { 159 u32 _nr_apqns, *_apqns = NULL; 160 int rc; 161 162 zcrypt_wait_api_operational(); 163 164 if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) { 165 u64 cur_mkvp = 0, old_mkvp = 0; 166 int minhwtype = ZCRYPT_CEX3C; 167 168 if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) 169 cur_mkvp = *((u64 *)cur_mkvp); 170 if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) 171 old_mkvp = *((u64 *)alt_mkvp); 172 if (ktype == PKEY_TYPE_CCA_CIPHER) 173 minhwtype = ZCRYPT_CEX6; 174 rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, 175 minhwtype, AES_MK_SET, 176 cur_mkvp, old_mkvp, 1); 177 if (rc) 178 goto out; 179 180 } else if (ktype == PKEY_TYPE_CCA_ECC) { 181 u64 cur_mkvp = 0, old_mkvp = 0; 182 183 if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) 184 cur_mkvp = *((u64 *)cur_mkvp); 185 if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) 186 old_mkvp = *((u64 *)alt_mkvp); 187 rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, 188 ZCRYPT_CEX7, APKA_MK_SET, 189 cur_mkvp, old_mkvp, 1); 190 if (rc) 191 goto out; 192 193 } else { 194 PKEY_DBF_ERR("%s unknown/unsupported key type %d", 195 __func__, (int)ktype); 196 return -EINVAL; 197 } 198 199 if (apqns) { 200 if (*nr_apqns < _nr_apqns) 201 rc = -ENOSPC; 202 else 203 memcpy(apqns, _apqns, _nr_apqns * sizeof(u32)); 204 } 205 *nr_apqns = _nr_apqns; 206 207 out: 208 kfree(_apqns); 209 pr_debug("rc=%d\n", rc); 210 return rc; 211 } 212 213 static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, 214 const u8 *key, u32 keylen, 215 u8 *protkey, u32 *protkeylen, u32 *protkeytype) 216 { 217 struct keytoken_header *hdr = (struct keytoken_header *)key; 218 struct pkey_apqn *local_apqns = NULL; 219 int i, rc; 220 221 if (keylen < sizeof(*hdr)) 222 return -EINVAL; 223 224 if (hdr->type == TOKTYPE_CCA_INTERNAL && 225 hdr->version == TOKVER_CCA_AES) { 226 /* CCA AES data key */ 227 if (keylen < sizeof(struct secaeskeytoken)) 228 return -EINVAL; 229 if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0)) 230 return -EINVAL; 231 } else if (hdr->type == TOKTYPE_CCA_INTERNAL && 232 hdr->version == TOKVER_CCA_VLSC) { 233 /* CCA AES cipher key */ 234 if (keylen < hdr->len) 235 return -EINVAL; 236 if (cca_check_secaescipherkey(pkey_dbf_info, 237 3, key, 0, 1)) 238 return -EINVAL; 239 } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { 240 /* CCA ECC (private) key */ 241 if (keylen < sizeof(struct eccprivkeytoken)) 242 return -EINVAL; 243 if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1)) 244 return -EINVAL; 245 } else { 246 PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n", 247 __func__, hdr->type, hdr->version); 248 return -EINVAL; 249 } 250 251 zcrypt_wait_api_operational(); 252 253 if (!apqns || (nr_apqns == 1 && 254 apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { 255 nr_apqns = MAXAPQNSINLIST; 256 local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), 257 GFP_KERNEL); 258 if (!local_apqns) 259 return -ENOMEM; 260 rc = cca_apqns4key(key, keylen, 0, local_apqns, &nr_apqns); 261 if (rc) 262 goto out; 263 apqns = local_apqns; 264 } 265 266 for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { 267 if (hdr->type == TOKTYPE_CCA_INTERNAL && 268 hdr->version == TOKVER_CCA_AES) { 269 rc = cca_sec2protkey(apqns[i].card, apqns[i].domain, 270 key, protkey, 271 protkeylen, protkeytype); 272 } else if (hdr->type == TOKTYPE_CCA_INTERNAL && 273 hdr->version == TOKVER_CCA_VLSC) { 274 rc = cca_cipher2protkey(apqns[i].card, apqns[i].domain, 275 key, protkey, 276 protkeylen, protkeytype); 277 } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { 278 rc = cca_ecc2protkey(apqns[i].card, apqns[i].domain, 279 key, protkey, 280 protkeylen, protkeytype); 281 } else { 282 rc = -EINVAL; 283 break; 284 } 285 } 286 287 out: 288 kfree(local_apqns); 289 pr_debug("rc=%d\n", rc); 290 return rc; 291 } 292 293 /* 294 * Generate CCA secure key. 295 * As of now only CCA AES Data or Cipher secure keys are 296 * supported. 297 * keytype is one of the PKEY_KEYTYPE_* constants, 298 * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER, 299 * keybitsize is the bit size of the key (may be 0 for 300 * keytype PKEY_KEYTYPE_AES_*). 301 */ 302 static int cca_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns, 303 u32 keytype, u32 subtype, 304 u32 keybitsize, u32 flags, 305 u8 *keybuf, u32 *keybuflen, u32 *_keyinfo) 306 { 307 struct pkey_apqn *local_apqns = NULL; 308 int i, len, rc; 309 310 /* check keytype, subtype, keybitsize */ 311 switch (keytype) { 312 case PKEY_KEYTYPE_AES_128: 313 case PKEY_KEYTYPE_AES_192: 314 case PKEY_KEYTYPE_AES_256: 315 len = pkey_keytype_aes_to_size(keytype); 316 if (keybitsize && keybitsize != 8 * len) { 317 PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n", 318 __func__, keybitsize); 319 return -EINVAL; 320 } 321 keybitsize = 8 * len; 322 switch (subtype) { 323 case PKEY_TYPE_CCA_DATA: 324 case PKEY_TYPE_CCA_CIPHER: 325 break; 326 default: 327 PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n", 328 __func__, subtype); 329 return -EINVAL; 330 } 331 break; 332 default: 333 PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n", 334 __func__, keytype); 335 return -EINVAL; 336 } 337 338 zcrypt_wait_api_operational(); 339 340 if (!apqns || (nr_apqns == 1 && 341 apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { 342 nr_apqns = MAXAPQNSINLIST; 343 local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), 344 GFP_KERNEL); 345 if (!local_apqns) 346 return -ENOMEM; 347 rc = cca_apqns4type(subtype, NULL, NULL, 0, 348 local_apqns, &nr_apqns); 349 if (rc) 350 goto out; 351 apqns = local_apqns; 352 } 353 354 for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { 355 if (subtype == PKEY_TYPE_CCA_CIPHER) { 356 rc = cca_gencipherkey(apqns[i].card, apqns[i].domain, 357 keybitsize, flags, 358 keybuf, keybuflen); 359 } else { 360 /* PKEY_TYPE_CCA_DATA */ 361 rc = cca_genseckey(apqns[i].card, apqns[i].domain, 362 keybitsize, keybuf); 363 *keybuflen = (rc ? 0 : SECKEYBLOBSIZE); 364 } 365 } 366 367 out: 368 kfree(local_apqns); 369 pr_debug("rc=%d\n", rc); 370 return rc; 371 } 372 373 /* 374 * Generate CCA secure key with given clear key value. 375 * As of now only CCA AES Data or Cipher secure keys are 376 * supported. 377 * keytype is one of the PKEY_KEYTYPE_* constants, 378 * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER, 379 * keybitsize is the bit size of the key (may be 0 for 380 * keytype PKEY_KEYTYPE_AES_*). 381 */ 382 static int cca_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns, 383 u32 keytype, u32 subtype, 384 u32 keybitsize, u32 flags, 385 const u8 *clrkey, u32 clrkeylen, 386 u8 *keybuf, u32 *keybuflen, u32 *_keyinfo) 387 { 388 struct pkey_apqn *local_apqns = NULL; 389 int i, len, rc; 390 391 /* check keytype, subtype, clrkeylen, keybitsize */ 392 switch (keytype) { 393 case PKEY_KEYTYPE_AES_128: 394 case PKEY_KEYTYPE_AES_192: 395 case PKEY_KEYTYPE_AES_256: 396 len = pkey_keytype_aes_to_size(keytype); 397 if (keybitsize && keybitsize != 8 * len) { 398 PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n", 399 __func__, keybitsize); 400 return -EINVAL; 401 } 402 keybitsize = 8 * len; 403 if (clrkeylen != len) { 404 PKEY_DBF_ERR("%s invalid clear key len %d != %d\n", 405 __func__, clrkeylen, len); 406 return -EINVAL; 407 } 408 switch (subtype) { 409 case PKEY_TYPE_CCA_DATA: 410 case PKEY_TYPE_CCA_CIPHER: 411 break; 412 default: 413 PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n", 414 __func__, subtype); 415 return -EINVAL; 416 } 417 break; 418 default: 419 PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n", 420 __func__, keytype); 421 return -EINVAL; 422 } 423 424 zcrypt_wait_api_operational(); 425 426 if (!apqns || (nr_apqns == 1 && 427 apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { 428 nr_apqns = MAXAPQNSINLIST; 429 local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), 430 GFP_KERNEL); 431 if (!local_apqns) 432 return -ENOMEM; 433 rc = cca_apqns4type(subtype, NULL, NULL, 0, 434 local_apqns, &nr_apqns); 435 if (rc) 436 goto out; 437 apqns = local_apqns; 438 } 439 440 for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { 441 if (subtype == PKEY_TYPE_CCA_CIPHER) { 442 rc = cca_clr2cipherkey(apqns[i].card, apqns[i].domain, 443 keybitsize, flags, clrkey, 444 keybuf, keybuflen); 445 } else { 446 /* PKEY_TYPE_CCA_DATA */ 447 rc = cca_clr2seckey(apqns[i].card, apqns[i].domain, 448 keybitsize, clrkey, keybuf); 449 *keybuflen = (rc ? 0 : SECKEYBLOBSIZE); 450 } 451 } 452 453 out: 454 kfree(local_apqns); 455 pr_debug("rc=%d\n", rc); 456 return rc; 457 } 458 459 static int cca_verifykey(const u8 *key, u32 keylen, 460 u16 *card, u16 *dom, 461 u32 *keytype, u32 *keybitsize, u32 *flags) 462 { 463 struct keytoken_header *hdr = (struct keytoken_header *)key; 464 u32 nr_apqns, *apqns = NULL; 465 int rc; 466 467 if (keylen < sizeof(*hdr)) 468 return -EINVAL; 469 470 zcrypt_wait_api_operational(); 471 472 if (hdr->type == TOKTYPE_CCA_INTERNAL && 473 hdr->version == TOKVER_CCA_AES) { 474 struct secaeskeytoken *t = (struct secaeskeytoken *)key; 475 476 rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0); 477 if (rc) 478 goto out; 479 *keytype = PKEY_TYPE_CCA_DATA; 480 *keybitsize = t->bitsize; 481 rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, 482 ZCRYPT_CEX3C, AES_MK_SET, 483 t->mkvp, 0, 1); 484 if (!rc) 485 *flags = PKEY_FLAGS_MATCH_CUR_MKVP; 486 if (rc == -ENODEV) { 487 rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, 488 ZCRYPT_CEX3C, AES_MK_SET, 489 0, t->mkvp, 1); 490 if (!rc) 491 *flags = PKEY_FLAGS_MATCH_ALT_MKVP; 492 } 493 if (rc) 494 goto out; 495 496 *card = ((struct pkey_apqn *)apqns)->card; 497 *dom = ((struct pkey_apqn *)apqns)->domain; 498 499 } else if (hdr->type == TOKTYPE_CCA_INTERNAL && 500 hdr->version == TOKVER_CCA_VLSC) { 501 struct cipherkeytoken *t = (struct cipherkeytoken *)key; 502 503 rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1); 504 if (rc) 505 goto out; 506 *keytype = PKEY_TYPE_CCA_CIPHER; 507 *keybitsize = PKEY_SIZE_UNKNOWN; 508 if (!t->plfver && t->wpllen == 512) 509 *keybitsize = PKEY_SIZE_AES_128; 510 else if (!t->plfver && t->wpllen == 576) 511 *keybitsize = PKEY_SIZE_AES_192; 512 else if (!t->plfver && t->wpllen == 640) 513 *keybitsize = PKEY_SIZE_AES_256; 514 rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, 515 ZCRYPT_CEX6, AES_MK_SET, 516 t->mkvp0, 0, 1); 517 if (!rc) 518 *flags = PKEY_FLAGS_MATCH_CUR_MKVP; 519 if (rc == -ENODEV) { 520 rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, 521 ZCRYPT_CEX6, AES_MK_SET, 522 0, t->mkvp0, 1); 523 if (!rc) 524 *flags = PKEY_FLAGS_MATCH_ALT_MKVP; 525 } 526 if (rc) 527 goto out; 528 529 *card = ((struct pkey_apqn *)apqns)->card; 530 *dom = ((struct pkey_apqn *)apqns)->domain; 531 532 } else { 533 /* unknown/unsupported key blob */ 534 rc = -EINVAL; 535 } 536 537 out: 538 kfree(apqns); 539 pr_debug("rc=%d\n", rc); 540 return rc; 541 } 542 543 /* 544 * This function provides an alternate but usually slow way 545 * to convert a 'clear key token' with AES key material into 546 * a protected key. This is done via an intermediate step 547 * which creates a CCA AES DATA secure key first and then 548 * derives the protected key from this secure key. 549 */ 550 static int cca_slowpath_key2protkey(const struct pkey_apqn *apqns, 551 size_t nr_apqns, 552 const u8 *key, u32 keylen, 553 u8 *protkey, u32 *protkeylen, 554 u32 *protkeytype) 555 { 556 const struct keytoken_header *hdr = (const struct keytoken_header *)key; 557 const struct clearkeytoken *t = (const struct clearkeytoken *)key; 558 u32 tmplen, keysize = 0; 559 u8 *tmpbuf; 560 int i, rc; 561 562 if (keylen < sizeof(*hdr)) 563 return -EINVAL; 564 565 if (hdr->type == TOKTYPE_NON_CCA && 566 hdr->version == TOKVER_CLEAR_KEY) 567 keysize = pkey_keytype_aes_to_size(t->keytype); 568 if (!keysize || t->len != keysize) 569 return -EINVAL; 570 571 /* alloc tmp key buffer */ 572 tmpbuf = kmalloc(SECKEYBLOBSIZE, GFP_ATOMIC); 573 if (!tmpbuf) 574 return -ENOMEM; 575 576 /* try two times in case of failure */ 577 for (i = 0, rc = -ENODEV; i < 2 && rc; i++) { 578 tmplen = SECKEYBLOBSIZE; 579 rc = cca_clr2key(NULL, 0, t->keytype, PKEY_TYPE_CCA_DATA, 580 8 * keysize, 0, t->clearkey, t->len, 581 tmpbuf, &tmplen, NULL); 582 pr_debug("cca_clr2key()=%d\n", rc); 583 if (rc) 584 continue; 585 rc = cca_key2protkey(NULL, 0, tmpbuf, tmplen, 586 protkey, protkeylen, protkeytype); 587 pr_debug("cca_key2protkey()=%d\n", rc); 588 } 589 590 kfree(tmpbuf); 591 pr_debug("rc=%d\n", rc); 592 return rc; 593 } 594 595 static struct pkey_handler cca_handler = { 596 .module = THIS_MODULE, 597 .name = "PKEY CCA handler", 598 .is_supported_key = is_cca_key, 599 .is_supported_keytype = is_cca_keytype, 600 .key_to_protkey = cca_key2protkey, 601 .slowpath_key_to_protkey = cca_slowpath_key2protkey, 602 .gen_key = cca_gen_key, 603 .clr_to_key = cca_clr2key, 604 .verify_key = cca_verifykey, 605 .apqns_for_key = cca_apqns4key, 606 .apqns_for_keytype = cca_apqns4type, 607 }; 608 609 /* 610 * Module init 611 */ 612 static int __init pkey_cca_init(void) 613 { 614 /* register this module as pkey handler for all the cca stuff */ 615 return pkey_handler_register(&cca_handler); 616 } 617 618 /* 619 * Module exit 620 */ 621 static void __exit pkey_cca_exit(void) 622 { 623 /* unregister this module as pkey handler */ 624 pkey_handler_unregister(&cca_handler); 625 } 626 627 module_init(pkey_cca_init); 628 module_exit(pkey_cca_exit); 629