]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - zfs/module/icp/core/kcf_mech_tabs.c
UBUNTU: SAUCE: (noup) Update spl to 0.7.3-1ubuntu1, zfs to 0.7.3-1ubuntu1
[mirror_ubuntu-bionic-kernel.git] / zfs / module / icp / core / kcf_mech_tabs.c
diff --git a/zfs/module/icp/core/kcf_mech_tabs.c b/zfs/module/icp/core/kcf_mech_tabs.c
new file mode 100644 (file)
index 0000000..723bfdb
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/zfs_context.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/api.h>
+#include <sys/crypto/impl.h>
+#include <sys/modhash.h>
+
+/* Cryptographic mechanisms tables and their access functions */
+
+/*
+ * Internal numbers assigned to mechanisms are coded as follows:
+ *
+ * +----------------+----------------+
+ * | mech. class    | mech. index    |
+ * <--- 32-bits --->+<--- 32-bits --->
+ *
+ * the mech_class identifies the table the mechanism belongs to.
+ * mech_index  is the index for that mechanism in the table.
+ * A mechanism belongs to exactly 1 table.
+ * The tables are:
+ * . digest_mechs_tab[] for the msg digest mechs.
+ * . cipher_mechs_tab[] for encrypt/decrypt and wrap/unwrap mechs.
+ * . mac_mechs_tab[] for MAC mechs.
+ * . sign_mechs_tab[] for sign & verify mechs.
+ * . keyops_mechs_tab[] for key/key pair generation, and key derivation.
+ * . misc_mechs_tab[] for mechs that don't belong to any of the above.
+ *
+ * There are no holes in the tables.
+ */
+
+/*
+ * Locking conventions:
+ * --------------------
+ * A global mutex, kcf_mech_tabs_lock, serializes writes to the
+ * mechanism table via kcf_create_mech_entry().
+ *
+ * A mutex is associated with every entry of the tables.
+ * The mutex is acquired whenever the entry is accessed for
+ * 1) retrieving the mech_id (comparing the mech name)
+ * 2) finding a provider for an xxx_init() or atomic operation.
+ * 3) altering the mechs entry to add or remove a provider.
+ *
+ * In 2), after a provider is chosen, its prov_desc is held and the
+ * entry's mutex must be dropped. The provider's working function (SPI) is
+ * called outside the mech_entry's mutex.
+ *
+ * The number of providers for a particular mechanism is not expected to be
+ * long enough to justify the cost of using rwlocks, so the per-mechanism
+ * entry mutex won't be very *hot*.
+ *
+ * When both kcf_mech_tabs_lock and a mech_entry mutex need to be held,
+ * kcf_mech_tabs_lock must always be acquired first.
+ *
+ */
+
+               /* Mechanisms tables */
+
+
+/* RFE 4687834 Will deal with the extensibility of these tables later */
+
+kcf_mech_entry_t kcf_digest_mechs_tab[KCF_MAXDIGEST];
+kcf_mech_entry_t kcf_cipher_mechs_tab[KCF_MAXCIPHER];
+kcf_mech_entry_t kcf_mac_mechs_tab[KCF_MAXMAC];
+kcf_mech_entry_t kcf_sign_mechs_tab[KCF_MAXSIGN];
+kcf_mech_entry_t kcf_keyops_mechs_tab[KCF_MAXKEYOPS];
+kcf_mech_entry_t kcf_misc_mechs_tab[KCF_MAXMISC];
+
+kcf_mech_entry_tab_t kcf_mech_tabs_tab[KCF_LAST_OPSCLASS + 1] = {
+       {0, NULL},                              /* No class zero */
+       {KCF_MAXDIGEST, kcf_digest_mechs_tab},
+       {KCF_MAXCIPHER, kcf_cipher_mechs_tab},
+       {KCF_MAXMAC, kcf_mac_mechs_tab},
+       {KCF_MAXSIGN, kcf_sign_mechs_tab},
+       {KCF_MAXKEYOPS, kcf_keyops_mechs_tab},
+       {KCF_MAXMISC, kcf_misc_mechs_tab}
+};
+
+/*
+ * Per-algorithm internal thresholds for the minimum input size of before
+ * offloading to hardware provider.
+ * Dispatching a crypto operation  to a hardware provider entails paying the
+ * cost of an additional context switch.  Measurments with Sun Accelerator 4000
+ * shows that 512-byte jobs or smaller are better handled in software.
+ * There is room for refinement here.
+ *
+ */
+int kcf_md5_threshold = 512;
+int kcf_sha1_threshold = 512;
+int kcf_des_threshold = 512;
+int kcf_des3_threshold = 512;
+int kcf_aes_threshold = 512;
+int kcf_bf_threshold = 512;
+int kcf_rc4_threshold = 512;
+
+kmutex_t kcf_mech_tabs_lock;
+static uint32_t kcf_gen_swprov = 0;
+
+int kcf_mech_hash_size = 256;
+mod_hash_t *kcf_mech_hash;     /* mech name to id hash */
+
+static crypto_mech_type_t
+kcf_mech_hash_find(char *mechname)
+{
+       mod_hash_val_t hv;
+       crypto_mech_type_t mt;
+
+       mt = CRYPTO_MECH_INVALID;
+       if (mod_hash_find(kcf_mech_hash, (mod_hash_key_t)mechname, &hv) == 0) {
+               mt = *(crypto_mech_type_t *)hv;
+               ASSERT(mt != CRYPTO_MECH_INVALID);
+       }
+
+       return (mt);
+}
+
+void
+kcf_destroy_mech_tabs(void)
+{
+       int i, max;
+       kcf_ops_class_t class;
+       kcf_mech_entry_t *me_tab;
+
+       if (kcf_mech_hash)
+               mod_hash_destroy_hash(kcf_mech_hash);
+
+       mutex_destroy(&kcf_mech_tabs_lock);
+
+       for (class = KCF_FIRST_OPSCLASS; class <= KCF_LAST_OPSCLASS; class++) {
+               max = kcf_mech_tabs_tab[class].met_size;
+               me_tab = kcf_mech_tabs_tab[class].met_tab;
+               for (i = 0; i < max; i++)
+                       mutex_destroy(&(me_tab[i].me_mutex));
+       }
+}
+
+/*
+ * kcf_init_mech_tabs()
+ *
+ * Called by the misc/kcf's _init() routine to initialize the tables
+ * of mech_entry's.
+ */
+void
+kcf_init_mech_tabs(void)
+{
+       int i, max;
+       kcf_ops_class_t class;
+       kcf_mech_entry_t *me_tab;
+
+       /* Initializes the mutex locks. */
+
+       mutex_init(&kcf_mech_tabs_lock, NULL, MUTEX_DEFAULT, NULL);
+
+       /* Then the pre-defined mechanism entries */
+
+       /* Two digests */
+       (void) strncpy(kcf_digest_mechs_tab[0].me_name, SUN_CKM_MD5,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_digest_mechs_tab[0].me_threshold = kcf_md5_threshold;
+
+       (void) strncpy(kcf_digest_mechs_tab[1].me_name, SUN_CKM_SHA1,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_digest_mechs_tab[1].me_threshold = kcf_sha1_threshold;
+
+       /* The symmetric ciphers in various modes */
+       (void) strncpy(kcf_cipher_mechs_tab[0].me_name, SUN_CKM_DES_CBC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[0].me_threshold = kcf_des_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[1].me_name, SUN_CKM_DES3_CBC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[1].me_threshold = kcf_des3_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[2].me_name, SUN_CKM_DES_ECB,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[2].me_threshold = kcf_des_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[3].me_name, SUN_CKM_DES3_ECB,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[3].me_threshold = kcf_des3_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[4].me_name, SUN_CKM_BLOWFISH_CBC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[4].me_threshold = kcf_bf_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[5].me_name, SUN_CKM_BLOWFISH_ECB,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[5].me_threshold = kcf_bf_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[6].me_name, SUN_CKM_AES_CBC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[6].me_threshold = kcf_aes_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[7].me_name, SUN_CKM_AES_ECB,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[7].me_threshold = kcf_aes_threshold;
+
+       (void) strncpy(kcf_cipher_mechs_tab[8].me_name, SUN_CKM_RC4,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_cipher_mechs_tab[8].me_threshold = kcf_rc4_threshold;
+
+
+       /* 4 HMACs */
+       (void) strncpy(kcf_mac_mechs_tab[0].me_name, SUN_CKM_MD5_HMAC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_mac_mechs_tab[0].me_threshold = kcf_md5_threshold;
+
+       (void) strncpy(kcf_mac_mechs_tab[1].me_name, SUN_CKM_MD5_HMAC_GENERAL,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_mac_mechs_tab[1].me_threshold = kcf_md5_threshold;
+
+       (void) strncpy(kcf_mac_mechs_tab[2].me_name, SUN_CKM_SHA1_HMAC,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_mac_mechs_tab[2].me_threshold = kcf_sha1_threshold;
+
+       (void) strncpy(kcf_mac_mechs_tab[3].me_name, SUN_CKM_SHA1_HMAC_GENERAL,
+           CRYPTO_MAX_MECH_NAME);
+       kcf_mac_mechs_tab[3].me_threshold = kcf_sha1_threshold;
+
+
+       /* 1 random number generation pseudo mechanism */
+       (void) strncpy(kcf_misc_mechs_tab[0].me_name, SUN_RANDOM,
+           CRYPTO_MAX_MECH_NAME);
+
+       kcf_mech_hash = mod_hash_create_strhash_nodtr("kcf mech2id hash",
+           kcf_mech_hash_size, mod_hash_null_valdtor);
+
+       for (class = KCF_FIRST_OPSCLASS; class <= KCF_LAST_OPSCLASS; class++) {
+               max = kcf_mech_tabs_tab[class].met_size;
+               me_tab = kcf_mech_tabs_tab[class].met_tab;
+               for (i = 0; i < max; i++) {
+                       mutex_init(&(me_tab[i].me_mutex), NULL,
+                           MUTEX_DEFAULT, NULL);
+                       if (me_tab[i].me_name[0] != 0) {
+                               me_tab[i].me_mechid = KCF_MECHID(class, i);
+                               (void) mod_hash_insert(kcf_mech_hash,
+                                   (mod_hash_key_t)me_tab[i].me_name,
+                                   (mod_hash_val_t)&(me_tab[i].me_mechid));
+                       }
+               }
+       }
+}
+
+/*
+ * kcf_create_mech_entry()
+ *
+ * Arguments:
+ *     . The class of mechanism.
+ *     . the name of the new mechanism.
+ *
+ * Description:
+ *     Creates a new mech_entry for a mechanism not yet known to the
+ *     framework.
+ *     This routine is called by kcf_add_mech_provider, which is
+ *     in turn invoked for each mechanism supported by a provider.
+ *     The'class' argument depends on the crypto_func_group_t bitmask
+ *     in the registering provider's mech_info struct for this mechanism.
+ *     When there is ambiguity in the mapping between the crypto_func_group_t
+ *     and a class (dual ops, ...) the KCF_MISC_CLASS should be used.
+ *
+ * Context:
+ *     User context only.
+ *
+ * Returns:
+ *     KCF_INVALID_MECH_CLASS or KCF_INVALID_MECH_NAME if the class or
+ *     the mechname is bogus.
+ *     KCF_MECH_TAB_FULL when there is no room left in the mech. tabs.
+ *     KCF_SUCCESS otherwise.
+ */
+static int
+kcf_create_mech_entry(kcf_ops_class_t class, char *mechname)
+{
+       crypto_mech_type_t mt;
+       kcf_mech_entry_t *me_tab;
+       int i = 0, size;
+
+       if ((class < KCF_FIRST_OPSCLASS) || (class > KCF_LAST_OPSCLASS))
+               return (KCF_INVALID_MECH_CLASS);
+
+       if ((mechname == NULL) || (mechname[0] == 0))
+               return (KCF_INVALID_MECH_NAME);
+       /*
+        * First check if the mechanism is already in one of the tables.
+        * The mech_entry could be in another class.
+        */
+       mutex_enter(&kcf_mech_tabs_lock);
+       mt = kcf_mech_hash_find(mechname);
+       if (mt != CRYPTO_MECH_INVALID) {
+               /* Nothing to do, regardless the suggested class. */
+               mutex_exit(&kcf_mech_tabs_lock);
+               return (KCF_SUCCESS);
+       }
+       /* Now take the next unused mech entry in the class's tab */
+       me_tab = kcf_mech_tabs_tab[class].met_tab;
+       size = kcf_mech_tabs_tab[class].met_size;
+
+       while (i < size) {
+               mutex_enter(&(me_tab[i].me_mutex));
+               if (me_tab[i].me_name[0] == 0) {
+                       /* Found an empty spot */
+                       (void) strncpy(me_tab[i].me_name, mechname,
+                           CRYPTO_MAX_MECH_NAME);
+                       me_tab[i].me_name[CRYPTO_MAX_MECH_NAME-1] = '\0';
+                       me_tab[i].me_mechid = KCF_MECHID(class, i);
+                       /*
+                        * No a-priori information about the new mechanism, so
+                        * the threshold is set to zero.
+                        */
+                       me_tab[i].me_threshold = 0;
+
+                       mutex_exit(&(me_tab[i].me_mutex));
+                       /* Add the new mechanism to the hash table */
+                       (void) mod_hash_insert(kcf_mech_hash,
+                           (mod_hash_key_t)me_tab[i].me_name,
+                           (mod_hash_val_t)&(me_tab[i].me_mechid));
+                       break;
+               }
+               mutex_exit(&(me_tab[i].me_mutex));
+               i++;
+       }
+
+       mutex_exit(&kcf_mech_tabs_lock);
+
+       if (i == size) {
+               return (KCF_MECH_TAB_FULL);
+       }
+
+       return (KCF_SUCCESS);
+}
+
+/*
+ * kcf_add_mech_provider()
+ *
+ * Arguments:
+ *     . An index in to  the provider mechanism array
+ *      . A pointer to the provider descriptor
+ *     . A storage for the kcf_prov_mech_desc_t the entry was added at.
+ *
+ * Description:
+ *      Adds  a new provider of a mechanism to the mechanism's mech_entry
+ *     chain.
+ *
+ * Context:
+ *      User context only.
+ *
+ * Returns
+ *      KCF_SUCCESS on success
+ *      KCF_MECH_TAB_FULL otherwise.
+ */
+int
+kcf_add_mech_provider(short mech_indx,
+    kcf_provider_desc_t *prov_desc, kcf_prov_mech_desc_t **pmdpp)
+{
+       int error;
+       kcf_mech_entry_t *mech_entry = NULL;
+       crypto_mech_info_t *mech_info;
+       crypto_mech_type_t kcf_mech_type, mt;
+       kcf_prov_mech_desc_t *prov_mech, *prov_mech2;
+       crypto_func_group_t simple_fg_mask, dual_fg_mask;
+       crypto_mech_info_t *dmi;
+       crypto_mech_info_list_t *mil, *mil2;
+       kcf_mech_entry_t *me;
+       int i;
+
+       ASSERT(prov_desc->pd_prov_type != CRYPTO_LOGICAL_PROVIDER);
+
+       mech_info = &prov_desc->pd_mechanisms[mech_indx];
+
+       /*
+        * A mechanism belongs to exactly one mechanism table.
+        * Find the class corresponding to the function group flag of
+        * the mechanism.
+        */
+       kcf_mech_type = kcf_mech_hash_find(mech_info->cm_mech_name);
+       if (kcf_mech_type == CRYPTO_MECH_INVALID) {
+               crypto_func_group_t fg = mech_info->cm_func_group_mask;
+               kcf_ops_class_t class;
+
+               if (fg & CRYPTO_FG_DIGEST || fg & CRYPTO_FG_DIGEST_ATOMIC)
+                       class = KCF_DIGEST_CLASS;
+               else if (fg & CRYPTO_FG_ENCRYPT || fg & CRYPTO_FG_DECRYPT ||
+                   fg & CRYPTO_FG_ENCRYPT_ATOMIC ||
+                   fg & CRYPTO_FG_DECRYPT_ATOMIC)
+                       class = KCF_CIPHER_CLASS;
+               else if (fg & CRYPTO_FG_MAC || fg & CRYPTO_FG_MAC_ATOMIC)
+                       class = KCF_MAC_CLASS;
+               else if (fg & CRYPTO_FG_SIGN || fg & CRYPTO_FG_VERIFY ||
+                   fg & CRYPTO_FG_SIGN_ATOMIC ||
+                   fg & CRYPTO_FG_VERIFY_ATOMIC ||
+                   fg & CRYPTO_FG_SIGN_RECOVER ||
+                   fg & CRYPTO_FG_VERIFY_RECOVER)
+                       class = KCF_SIGN_CLASS;
+               else if (fg & CRYPTO_FG_GENERATE ||
+                   fg & CRYPTO_FG_GENERATE_KEY_PAIR ||
+                   fg & CRYPTO_FG_WRAP || fg & CRYPTO_FG_UNWRAP ||
+                   fg & CRYPTO_FG_DERIVE)
+                       class = KCF_KEYOPS_CLASS;
+               else
+                       class = KCF_MISC_CLASS;
+
+               /*
+                * Attempt to create a new mech_entry for the specified
+                * mechanism. kcf_create_mech_entry() can handle the case
+                * where such an entry already exists.
+                */
+               if ((error = kcf_create_mech_entry(class,
+                   mech_info->cm_mech_name)) != KCF_SUCCESS) {
+                       return (error);
+               }
+               /* get the KCF mech type that was assigned to the mechanism */
+               kcf_mech_type = kcf_mech_hash_find(mech_info->cm_mech_name);
+               ASSERT(kcf_mech_type != CRYPTO_MECH_INVALID);
+       }
+
+       error = kcf_get_mech_entry(kcf_mech_type, &mech_entry);
+       ASSERT(error == KCF_SUCCESS);
+
+       /* allocate and initialize new kcf_prov_mech_desc */
+       prov_mech = kmem_zalloc(sizeof (kcf_prov_mech_desc_t), KM_SLEEP);
+       bcopy(mech_info, &prov_mech->pm_mech_info, sizeof (crypto_mech_info_t));
+       prov_mech->pm_prov_desc = prov_desc;
+       prov_desc->pd_mech_indx[KCF_MECH2CLASS(kcf_mech_type)]
+           [KCF_MECH2INDEX(kcf_mech_type)] = mech_indx;
+
+       KCF_PROV_REFHOLD(prov_desc);
+       KCF_PROV_IREFHOLD(prov_desc);
+
+       dual_fg_mask = mech_info->cm_func_group_mask & CRYPTO_FG_DUAL_MASK;
+
+       if (dual_fg_mask == ((crypto_func_group_t)0))
+               goto add_entry;
+
+       simple_fg_mask = (mech_info->cm_func_group_mask &
+           CRYPTO_FG_SIMPLEOP_MASK) | CRYPTO_FG_RANDOM;
+
+       for (i = 0; i < prov_desc->pd_mech_list_count; i++) {
+               dmi = &prov_desc->pd_mechanisms[i];
+
+               /* skip self */
+               if (dmi->cm_mech_number == mech_info->cm_mech_number)
+                       continue;
+
+               /* skip if not a dual operation mechanism */
+               if (!(dmi->cm_func_group_mask & dual_fg_mask) ||
+                   (dmi->cm_func_group_mask & simple_fg_mask))
+                       continue;
+
+               mt = kcf_mech_hash_find(dmi->cm_mech_name);
+               if (mt == CRYPTO_MECH_INVALID)
+                       continue;
+
+               if (kcf_get_mech_entry(mt, &me) != KCF_SUCCESS)
+                       continue;
+
+               mil = kmem_zalloc(sizeof (*mil), KM_SLEEP);
+               mil2 = kmem_zalloc(sizeof (*mil2), KM_SLEEP);
+
+               /*
+                * Ignore hard-coded entries in the mech table
+                * if the provider hasn't registered.
+                */
+               mutex_enter(&me->me_mutex);
+               if (me->me_hw_prov_chain == NULL && me->me_sw_prov == NULL) {
+                       mutex_exit(&me->me_mutex);
+                       kmem_free(mil, sizeof (*mil));
+                       kmem_free(mil2, sizeof (*mil2));
+                       continue;
+               }
+
+               /*
+                * Add other dual mechanisms that have registered
+                * with the framework to this mechanism's
+                * cross-reference list.
+                */
+               mil->ml_mech_info = *dmi; /* struct assignment */
+               mil->ml_kcf_mechid = mt;
+
+               /* add to head of list */
+               mil->ml_next = prov_mech->pm_mi_list;
+               prov_mech->pm_mi_list = mil;
+
+               if (prov_desc->pd_prov_type == CRYPTO_HW_PROVIDER)
+                       prov_mech2 = me->me_hw_prov_chain;
+               else
+                       prov_mech2 = me->me_sw_prov;
+
+               if (prov_mech2 == NULL) {
+                       kmem_free(mil2, sizeof (*mil2));
+                       mutex_exit(&me->me_mutex);
+                       continue;
+               }
+
+               /*
+                * Update all other cross-reference lists by
+                * adding this new mechanism.
+                */
+               while (prov_mech2 != NULL) {
+                       if (prov_mech2->pm_prov_desc == prov_desc) {
+                               /* struct assignment */
+                               mil2->ml_mech_info = *mech_info;
+                               mil2->ml_kcf_mechid = kcf_mech_type;
+
+                               /* add to head of list */
+                               mil2->ml_next = prov_mech2->pm_mi_list;
+                               prov_mech2->pm_mi_list = mil2;
+                               break;
+                       }
+                       prov_mech2 = prov_mech2->pm_next;
+               }
+               if (prov_mech2 == NULL)
+                       kmem_free(mil2, sizeof (*mil2));
+
+               mutex_exit(&me->me_mutex);
+       }
+
+add_entry:
+       /*
+        * Add new kcf_prov_mech_desc at the front of HW providers
+        * chain.
+        */
+       switch (prov_desc->pd_prov_type) {
+
+       case CRYPTO_HW_PROVIDER:
+               mutex_enter(&mech_entry->me_mutex);
+               prov_mech->pm_me = mech_entry;
+               prov_mech->pm_next = mech_entry->me_hw_prov_chain;
+               mech_entry->me_hw_prov_chain = prov_mech;
+               mech_entry->me_num_hwprov++;
+               mutex_exit(&mech_entry->me_mutex);
+               break;
+
+       case CRYPTO_SW_PROVIDER:
+               mutex_enter(&mech_entry->me_mutex);
+               if (mech_entry->me_sw_prov != NULL) {
+                       /*
+                        * There is already a SW provider for this mechanism.
+                        * Since we allow only one SW provider per mechanism,
+                        * report this condition.
+                        */
+                       cmn_err(CE_WARN, "The cryptographic software provider "
+                           "\"%s\" will not be used for %s. The provider "
+                           "\"%s\" will be used for this mechanism "
+                           "instead.", prov_desc->pd_description,
+                           mech_info->cm_mech_name,
+                           mech_entry->me_sw_prov->pm_prov_desc->
+                           pd_description);
+                       KCF_PROV_REFRELE(prov_desc);
+                       kmem_free(prov_mech, sizeof (kcf_prov_mech_desc_t));
+                       prov_mech = NULL;
+               } else {
+                       /*
+                        * Set the provider as the software provider for
+                        * this mechanism.
+                        */
+                       mech_entry->me_sw_prov = prov_mech;
+
+                       /* We'll wrap around after 4 billion registrations! */
+                       mech_entry->me_gen_swprov = kcf_gen_swprov++;
+               }
+               mutex_exit(&mech_entry->me_mutex);
+               break;
+       default:
+               break;
+       }
+
+       *pmdpp = prov_mech;
+
+       return (KCF_SUCCESS);
+}
+
+/*
+ * kcf_remove_mech_provider()
+ *
+ * Arguments:
+ *      . mech_name: the name of the mechanism.
+ *      . prov_desc: The provider descriptor
+ *
+ * Description:
+ *      Removes a provider from chain of provider descriptors.
+ *     The provider is made unavailable to kernel consumers for the specified
+ *     mechanism.
+ *
+ * Context:
+ *      User context only.
+ */
+void
+kcf_remove_mech_provider(char *mech_name, kcf_provider_desc_t *prov_desc)
+{
+       crypto_mech_type_t mech_type;
+       kcf_prov_mech_desc_t *prov_mech = NULL, *prov_chain;
+       kcf_prov_mech_desc_t **prev_entry_next;
+       kcf_mech_entry_t *mech_entry;
+       crypto_mech_info_list_t *mil, *mil2, *next, **prev_next;
+
+       ASSERT(prov_desc->pd_prov_type != CRYPTO_LOGICAL_PROVIDER);
+
+       /* get the KCF mech type that was assigned to the mechanism */
+       if ((mech_type = kcf_mech_hash_find(mech_name)) ==
+           CRYPTO_MECH_INVALID) {
+               /*
+                * Provider was not allowed for this mech due to policy or
+                * configuration.
+                */
+               return;
+       }
+
+       /* get a ptr to the mech_entry that was created */
+       if (kcf_get_mech_entry(mech_type, &mech_entry) != KCF_SUCCESS) {
+               /*
+                * Provider was not allowed for this mech due to policy or
+                * configuration.
+                */
+               return;
+       }
+
+       mutex_enter(&mech_entry->me_mutex);
+
+       switch (prov_desc->pd_prov_type) {
+
+       case CRYPTO_HW_PROVIDER:
+               /* find the provider in the mech_entry chain */
+               prev_entry_next = &mech_entry->me_hw_prov_chain;
+               prov_mech = mech_entry->me_hw_prov_chain;
+               while (prov_mech != NULL &&
+                   prov_mech->pm_prov_desc != prov_desc) {
+                       prev_entry_next = &prov_mech->pm_next;
+                       prov_mech = prov_mech->pm_next;
+               }
+
+               if (prov_mech == NULL) {
+                       /* entry not found, simply return */
+                       mutex_exit(&mech_entry->me_mutex);
+                       return;
+               }
+
+               /* remove provider entry from mech_entry chain */
+               *prev_entry_next = prov_mech->pm_next;
+               ASSERT(mech_entry->me_num_hwprov > 0);
+               mech_entry->me_num_hwprov--;
+               break;
+
+       case CRYPTO_SW_PROVIDER:
+               if (mech_entry->me_sw_prov == NULL ||
+                   mech_entry->me_sw_prov->pm_prov_desc != prov_desc) {
+                       /* not the software provider for this mechanism */
+                       mutex_exit(&mech_entry->me_mutex);
+                       return;
+               }
+               prov_mech = mech_entry->me_sw_prov;
+               mech_entry->me_sw_prov = NULL;
+               break;
+       default:
+               /* unexpected crypto_provider_type_t */
+               mutex_exit(&mech_entry->me_mutex);
+               return;
+       }
+
+       mutex_exit(&mech_entry->me_mutex);
+
+       /* Free the dual ops cross-reference lists  */
+       mil = prov_mech->pm_mi_list;
+       while (mil != NULL) {
+               next = mil->ml_next;
+               if (kcf_get_mech_entry(mil->ml_kcf_mechid,
+                   &mech_entry) != KCF_SUCCESS) {
+                       mil = next;
+                       continue;
+               }
+
+               mutex_enter(&mech_entry->me_mutex);
+               if (prov_desc->pd_prov_type == CRYPTO_HW_PROVIDER)
+                       prov_chain = mech_entry->me_hw_prov_chain;
+               else
+                       prov_chain = mech_entry->me_sw_prov;
+
+               while (prov_chain != NULL) {
+                       if (prov_chain->pm_prov_desc == prov_desc) {
+                               prev_next = &prov_chain->pm_mi_list;
+                               mil2 = prov_chain->pm_mi_list;
+                               while (mil2 != NULL &&
+                                   mil2->ml_kcf_mechid != mech_type) {
+                                       prev_next = &mil2->ml_next;
+                                       mil2 = mil2->ml_next;
+                               }
+                               if (mil2 != NULL) {
+                                       *prev_next = mil2->ml_next;
+                                       kmem_free(mil2, sizeof (*mil2));
+                               }
+                               break;
+                       }
+                       prov_chain = prov_chain->pm_next;
+               }
+
+               mutex_exit(&mech_entry->me_mutex);
+               kmem_free(mil, sizeof (crypto_mech_info_list_t));
+               mil = next;
+       }
+
+       /* free entry  */
+       KCF_PROV_REFRELE(prov_mech->pm_prov_desc);
+       KCF_PROV_IREFRELE(prov_mech->pm_prov_desc);
+       kmem_free(prov_mech, sizeof (kcf_prov_mech_desc_t));
+}
+
+/*
+ * kcf_get_mech_entry()
+ *
+ * Arguments:
+ *      . The framework mechanism type
+ *      . Storage for the mechanism entry
+ *
+ * Description:
+ *      Retrieves the mechanism entry for the mech.
+ *
+ * Context:
+ *      User and interrupt contexts.
+ *
+ * Returns:
+ *      KCF_MECHANISM_XXX appropriate error code.
+ *      KCF_SUCCESS otherwise.
+ */
+int
+kcf_get_mech_entry(crypto_mech_type_t mech_type, kcf_mech_entry_t **mep)
+{
+       kcf_ops_class_t         class;
+       int                     index;
+       kcf_mech_entry_tab_t    *me_tab;
+
+       ASSERT(mep != NULL);
+
+       class = KCF_MECH2CLASS(mech_type);
+
+       if ((class < KCF_FIRST_OPSCLASS) || (class > KCF_LAST_OPSCLASS)) {
+               /* the caller won't need to know it's an invalid class */
+               return (KCF_INVALID_MECH_NUMBER);
+       }
+
+       me_tab = &kcf_mech_tabs_tab[class];
+       index = KCF_MECH2INDEX(mech_type);
+
+       if ((index < 0) || (index >= me_tab->met_size)) {
+               return (KCF_INVALID_MECH_NUMBER);
+       }
+
+       *mep = &((me_tab->met_tab)[index]);
+
+       return (KCF_SUCCESS);
+}
+
+/* CURRENTLY UNSUPPORTED: attempting to load the module if it isn't found */
+/*
+ * Lookup the hash table for an entry that matches the mechname.
+ * If there are no hardware or software providers for the mechanism,
+ * but there is an unloaded software provider, this routine will attempt
+ * to load it.
+ *
+ * If the MOD_NOAUTOUNLOAD flag is not set, a software provider is
+ * in constant danger of being unloaded.  For consumers that call
+ * crypto_mech2id() only once, the provider will not be reloaded
+ * if it becomes unloaded.  If a provider gets loaded elsewhere
+ * without the MOD_NOAUTOUNLOAD flag being set, we set it now.
+ */
+crypto_mech_type_t
+crypto_mech2id_common(char *mechname, boolean_t load_module)
+{
+       crypto_mech_type_t mt = kcf_mech_hash_find(mechname);
+       return (mt);
+}