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