363 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * pkey base: debug feature, pkey handler registry4 *5 * Copyright IBM Corp. 20246 */7 8#define KMSG_COMPONENT "pkey"9#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt10 11#include <linux/cpufeature.h>12#include <linux/init.h>13#include <linux/list.h>14#include <linux/module.h>15#include <linux/rculist.h>16 17#include "pkey_base.h"18 19MODULE_LICENSE("GPL");20MODULE_AUTHOR("IBM Corporation");21MODULE_DESCRIPTION("s390 protected key base and api");22 23/*24 * pkey debug feature25 */26debug_info_t *pkey_dbf_info;27EXPORT_SYMBOL(pkey_dbf_info);28 29/*30 * pkey handler registry31 */32 33static DEFINE_SPINLOCK(handler_list_write_lock);34static LIST_HEAD(handler_list);35 36int pkey_handler_register(struct pkey_handler *handler)37{38 const struct pkey_handler *h;39 40 if (!handler ||41 !handler->is_supported_key ||42 !handler->is_supported_keytype)43 return -EINVAL;44 45 if (!try_module_get(handler->module))46 return -ENXIO;47 48 spin_lock(&handler_list_write_lock);49 50 rcu_read_lock();51 list_for_each_entry_rcu(h, &handler_list, list) {52 if (h == handler) {53 rcu_read_unlock();54 spin_unlock(&handler_list_write_lock);55 module_put(handler->module);56 return -EEXIST;57 }58 }59 rcu_read_unlock();60 61 list_add_rcu(&handler->list, &handler_list);62 spin_unlock(&handler_list_write_lock);63 synchronize_rcu();64 65 module_put(handler->module);66 67 PKEY_DBF_INFO("%s pkey handler '%s' registered\n", __func__,68 handler->name ?: "<no name>");69 70 return 0;71}72EXPORT_SYMBOL(pkey_handler_register);73 74int pkey_handler_unregister(struct pkey_handler *handler)75{76 spin_lock(&handler_list_write_lock);77 list_del_rcu(&handler->list);78 INIT_LIST_HEAD_RCU(&handler->list);79 spin_unlock(&handler_list_write_lock);80 synchronize_rcu();81 82 PKEY_DBF_INFO("%s pkey handler '%s' unregistered\n", __func__,83 handler->name ?: "<no name>");84 85 return 0;86}87EXPORT_SYMBOL(pkey_handler_unregister);88 89/*90 * Handler invocation functions.91 */92 93const struct pkey_handler *pkey_handler_get_keybased(const u8 *key, u32 keylen)94{95 const struct pkey_handler *h;96 97 rcu_read_lock();98 list_for_each_entry_rcu(h, &handler_list, list) {99 if (!try_module_get(h->module))100 continue;101 if (h->is_supported_key(key, keylen)) {102 rcu_read_unlock();103 return h;104 }105 module_put(h->module);106 }107 rcu_read_unlock();108 109 return NULL;110}111EXPORT_SYMBOL(pkey_handler_get_keybased);112 113const struct pkey_handler *pkey_handler_get_keytypebased(enum pkey_key_type kt)114{115 const struct pkey_handler *h;116 117 rcu_read_lock();118 list_for_each_entry_rcu(h, &handler_list, list) {119 if (!try_module_get(h->module))120 continue;121 if (h->is_supported_keytype(kt)) {122 rcu_read_unlock();123 return h;124 }125 module_put(h->module);126 }127 rcu_read_unlock();128 129 return NULL;130}131EXPORT_SYMBOL(pkey_handler_get_keytypebased);132 133void pkey_handler_put(const struct pkey_handler *handler)134{135 const struct pkey_handler *h;136 137 if (!handler)138 return;139 140 rcu_read_lock();141 list_for_each_entry_rcu(h, &handler_list, list) {142 if (h == handler) {143 module_put(h->module);144 break;145 }146 }147 rcu_read_unlock();148}149EXPORT_SYMBOL(pkey_handler_put);150 151int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,152 const u8 *key, u32 keylen,153 u8 *protkey, u32 *protkeylen, u32 *protkeytype)154{155 const struct pkey_handler *h;156 int rc = -ENODEV;157 158 h = pkey_handler_get_keybased(key, keylen);159 if (h && h->key_to_protkey) {160 rc = h->key_to_protkey(apqns, nr_apqns, key, keylen,161 protkey, protkeylen,162 protkeytype);163 }164 pkey_handler_put(h);165 166 return rc;167}168EXPORT_SYMBOL(pkey_handler_key_to_protkey);169 170/*171 * This handler invocation is special as there may be more than172 * one handler providing support for the very same key (type).173 * And the handler may not respond true on is_supported_key(),174 * so simple try and check return value here.175 */176int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,177 size_t nr_apqns,178 const u8 *key, u32 keylen,179 u8 *protkey, u32 *protkeylen,180 u32 *protkeytype)181{182 const struct pkey_handler *h, *htmp[10];183 int i, n = 0, rc = -ENODEV;184 185 rcu_read_lock();186 list_for_each_entry_rcu(h, &handler_list, list) {187 if (!try_module_get(h->module))188 continue;189 if (h->slowpath_key_to_protkey && n < ARRAY_SIZE(htmp))190 htmp[n++] = h;191 else192 module_put(h->module);193 }194 rcu_read_unlock();195 196 for (i = 0; i < n; i++) {197 h = htmp[i];198 if (rc)199 rc = h->slowpath_key_to_protkey(apqns, nr_apqns,200 key, keylen,201 protkey, protkeylen,202 protkeytype);203 module_put(h->module);204 }205 206 return rc;207}208EXPORT_SYMBOL(pkey_handler_slowpath_key_to_protkey);209 210int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,211 u32 keytype, u32 keysubtype,212 u32 keybitsize, u32 flags,213 u8 *keybuf, u32 *keybuflen, u32 *keyinfo)214{215 const struct pkey_handler *h;216 int rc = -ENODEV;217 218 h = pkey_handler_get_keytypebased(keysubtype);219 if (h && h->gen_key) {220 rc = h->gen_key(apqns, nr_apqns, keytype, keysubtype,221 keybitsize, flags,222 keybuf, keybuflen, keyinfo);223 }224 pkey_handler_put(h);225 226 return rc;227}228EXPORT_SYMBOL(pkey_handler_gen_key);229 230int pkey_handler_clr_to_key(const struct pkey_apqn *apqns, size_t nr_apqns,231 u32 keytype, u32 keysubtype,232 u32 keybitsize, u32 flags,233 const u8 *clrkey, u32 clrkeylen,234 u8 *keybuf, u32 *keybuflen, u32 *keyinfo)235{236 const struct pkey_handler *h;237 int rc = -ENODEV;238 239 h = pkey_handler_get_keytypebased(keysubtype);240 if (h && h->clr_to_key) {241 rc = h->clr_to_key(apqns, nr_apqns, keytype, keysubtype,242 keybitsize, flags, clrkey, clrkeylen,243 keybuf, keybuflen, keyinfo);244 }245 pkey_handler_put(h);246 247 return rc;248}249EXPORT_SYMBOL(pkey_handler_clr_to_key);250 251int pkey_handler_verify_key(const u8 *key, u32 keylen,252 u16 *card, u16 *dom,253 u32 *keytype, u32 *keybitsize, u32 *flags)254{255 const struct pkey_handler *h;256 int rc = -ENODEV;257 258 h = pkey_handler_get_keybased(key, keylen);259 if (h && h->verify_key) {260 rc = h->verify_key(key, keylen, card, dom,261 keytype, keybitsize, flags);262 }263 pkey_handler_put(h);264 265 return rc;266}267EXPORT_SYMBOL(pkey_handler_verify_key);268 269int pkey_handler_apqns_for_key(const u8 *key, u32 keylen, u32 flags,270 struct pkey_apqn *apqns, size_t *nr_apqns)271{272 const struct pkey_handler *h;273 int rc = -ENODEV;274 275 h = pkey_handler_get_keybased(key, keylen);276 if (h && h->apqns_for_key)277 rc = h->apqns_for_key(key, keylen, flags, apqns, nr_apqns);278 pkey_handler_put(h);279 280 return rc;281}282EXPORT_SYMBOL(pkey_handler_apqns_for_key);283 284int pkey_handler_apqns_for_keytype(enum pkey_key_type keysubtype,285 u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,286 struct pkey_apqn *apqns, size_t *nr_apqns)287{288 const struct pkey_handler *h;289 int rc = -ENODEV;290 291 h = pkey_handler_get_keytypebased(keysubtype);292 if (h && h->apqns_for_keytype) {293 rc = h->apqns_for_keytype(keysubtype,294 cur_mkvp, alt_mkvp, flags,295 apqns, nr_apqns);296 }297 pkey_handler_put(h);298 299 return rc;300}301EXPORT_SYMBOL(pkey_handler_apqns_for_keytype);302 303void pkey_handler_request_modules(void)304{305#ifdef CONFIG_MODULES306 static const char * const pkey_handler_modules[] = {307 "pkey_cca", "pkey_ep11", "pkey_pckmo" };308 int i;309 310 for (i = 0; i < ARRAY_SIZE(pkey_handler_modules); i++) {311 const struct pkey_handler *h;312 bool found = false;313 314 rcu_read_lock();315 list_for_each_entry_rcu(h, &handler_list, list) {316 if (h->module &&317 !strcmp(h->module->name, pkey_handler_modules[i])) {318 found = true;319 break;320 }321 }322 rcu_read_unlock();323 if (!found) {324 pr_debug("request_module(%s)\n", pkey_handler_modules[i]);325 request_module(pkey_handler_modules[i]);326 }327 }328#endif329}330EXPORT_SYMBOL(pkey_handler_request_modules);331 332/*333 * Module init334 */335static int __init pkey_init(void)336{337 int rc;338 339 /* init debug feature */340 pkey_dbf_info = debug_register("pkey", 1, 1, 5 * sizeof(long));341 debug_register_view(pkey_dbf_info, &debug_sprintf_view);342 debug_set_level(pkey_dbf_info, 4);343 344 /* the handler registry does not need any init */345 346 rc = pkey_api_init();347 if (rc)348 debug_unregister(pkey_dbf_info);349 350 return rc;351}352 353/*354 * Module exit355 */356static void __exit pkey_exit(void)357{358 pkey_api_exit();359}360 361module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_init);362module_exit(pkey_exit);363