14d703b5cSMark Powers /* 24d703b5cSMark Powers * CDDL HEADER START 34d703b5cSMark Powers * 44d703b5cSMark Powers * The contents of this file are subject to the terms of the 54d703b5cSMark Powers * Common Development and Distribution License (the "License"). 64d703b5cSMark Powers * You may not use this file except in compliance with the License. 74d703b5cSMark Powers * 84d703b5cSMark Powers * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94d703b5cSMark Powers * or http://www.opensolaris.org/os/licensing. 104d703b5cSMark Powers * See the License for the specific language governing permissions 114d703b5cSMark Powers * and limitations under the License. 124d703b5cSMark Powers * 134d703b5cSMark Powers * When distributing Covered Code, include this CDDL HEADER in each 144d703b5cSMark Powers * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154d703b5cSMark Powers * If applicable, add the following below this CDDL HEADER, with the 164d703b5cSMark Powers * fields enclosed by brackets "[]" replaced with your own identifying 174d703b5cSMark Powers * information: Portions Copyright [yyyy] [name of copyright owner] 184d703b5cSMark Powers * 194d703b5cSMark Powers * CDDL HEADER END 204d703b5cSMark Powers */ 214d703b5cSMark Powers /* 22e8c016efSMark Powers * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 234d703b5cSMark Powers * Use is subject to license terms. 244d703b5cSMark Powers */ 254d703b5cSMark Powers 26*104d3bdeSDan OpenSolaris Anderson 274d703b5cSMark Powers #ifndef _KERNEL 284d703b5cSMark Powers #include <strings.h> 294d703b5cSMark Powers #include <limits.h> 304d703b5cSMark Powers #include <assert.h> 314d703b5cSMark Powers #include <security/cryptoki.h> 32*104d3bdeSDan OpenSolaris Anderson #endif /* _KERNEL */ 33*104d3bdeSDan OpenSolaris Anderson 344d703b5cSMark Powers 354d703b5cSMark Powers #include <sys/types.h> 364d703b5cSMark Powers #include <sys/kmem.h> 374d703b5cSMark Powers #include <modes/modes.h> 384d703b5cSMark Powers #include <sys/crypto/common.h> 394d703b5cSMark Powers #include <sys/crypto/impl.h> 404d703b5cSMark Powers #include <sys/byteorder.h> 414d703b5cSMark Powers 42*104d3bdeSDan OpenSolaris Anderson #ifdef __amd64 43*104d3bdeSDan OpenSolaris Anderson #include <sys/x86_archext.h> /* x86_feature, X86_*, CPUID_* */ 44*104d3bdeSDan OpenSolaris Anderson 45*104d3bdeSDan OpenSolaris Anderson #ifndef _KERNEL 46*104d3bdeSDan OpenSolaris Anderson #include <sys/cpuvar.h> /* cpu_t, CPU */ 47*104d3bdeSDan OpenSolaris Anderson #include <sys/disp.h> /* kpreempt_disable(), kpreempt_enable */ 48*104d3bdeSDan OpenSolaris Anderson /* Workaround for no XMM kernel thread save/restore */ 49*104d3bdeSDan OpenSolaris Anderson #define KPREEMPT_DISABLE kpreempt_disable() 50*104d3bdeSDan OpenSolaris Anderson #define KPREEMPT_ENABLE kpreempt_enable() 51*104d3bdeSDan OpenSolaris Anderson 52*104d3bdeSDan OpenSolaris Anderson #else 53*104d3bdeSDan OpenSolaris Anderson #include <sys/auxv.h> /* getisax() */ 54*104d3bdeSDan OpenSolaris Anderson #include <sys/auxv_386.h> /* AV_386_PCLMULQDQ bit */ 55*104d3bdeSDan OpenSolaris Anderson #define KPREEMPT_DISABLE 56*104d3bdeSDan OpenSolaris Anderson #define KPREEMPT_ENABLE 57*104d3bdeSDan OpenSolaris Anderson #endif /* _KERNEL */ 58*104d3bdeSDan OpenSolaris Anderson 59*104d3bdeSDan OpenSolaris Anderson extern void gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res); 60*104d3bdeSDan OpenSolaris Anderson static int intel_pclmulqdq_instruction_present(void); 61*104d3bdeSDan OpenSolaris Anderson #endif /* __amd64 */ 62*104d3bdeSDan OpenSolaris Anderson 634d703b5cSMark Powers struct aes_block { 644d703b5cSMark Powers uint64_t a; 654d703b5cSMark Powers uint64_t b; 664d703b5cSMark Powers }; 674d703b5cSMark Powers 68*104d3bdeSDan OpenSolaris Anderson 69*104d3bdeSDan OpenSolaris Anderson /* 70*104d3bdeSDan OpenSolaris Anderson * gcm_mul() 71*104d3bdeSDan OpenSolaris Anderson * Perform a carry-less multiplication (that is, use XOR instead of the 72*104d3bdeSDan OpenSolaris Anderson * multiply operator) on *x_in and *y and place the result in *res. 73*104d3bdeSDan OpenSolaris Anderson * 74*104d3bdeSDan OpenSolaris Anderson * Byte swap the input (*x_in and *y) and the output (*res). 75*104d3bdeSDan OpenSolaris Anderson * 76*104d3bdeSDan OpenSolaris Anderson * Note: x_in, y, and res all point to 16-byte numbers (an array of two 77*104d3bdeSDan OpenSolaris Anderson * 64-bit integers). 78*104d3bdeSDan OpenSolaris Anderson */ 79e8c016efSMark Powers void 804d703b5cSMark Powers gcm_mul(uint64_t *x_in, uint64_t *y, uint64_t *res) 814d703b5cSMark Powers { 82*104d3bdeSDan OpenSolaris Anderson #ifdef __amd64 83*104d3bdeSDan OpenSolaris Anderson if (intel_pclmulqdq_instruction_present()) { 84*104d3bdeSDan OpenSolaris Anderson KPREEMPT_DISABLE; 85*104d3bdeSDan OpenSolaris Anderson gcm_mul_pclmulqdq(x_in, y, res); 86*104d3bdeSDan OpenSolaris Anderson KPREEMPT_ENABLE; 87*104d3bdeSDan OpenSolaris Anderson } else 88*104d3bdeSDan OpenSolaris Anderson #endif /* __amd64 */ 89*104d3bdeSDan OpenSolaris Anderson { 90*104d3bdeSDan OpenSolaris Anderson static const uint64_t R = 0xe100000000000000ULL; 914d703b5cSMark Powers struct aes_block z = {0, 0}; 924d703b5cSMark Powers struct aes_block v; 934d703b5cSMark Powers uint64_t x; 944d703b5cSMark Powers int i, j; 954d703b5cSMark Powers 964d703b5cSMark Powers v.a = ntohll(y[0]); 974d703b5cSMark Powers v.b = ntohll(y[1]); 984d703b5cSMark Powers 994d703b5cSMark Powers for (j = 0; j < 2; j++) { 1004d703b5cSMark Powers x = ntohll(x_in[j]); 1014d703b5cSMark Powers for (i = 0; i < 64; i++, x <<= 1) { 1024d703b5cSMark Powers if (x & 0x8000000000000000ULL) { 1034d703b5cSMark Powers z.a ^= v.a; 1044d703b5cSMark Powers z.b ^= v.b; 1054d703b5cSMark Powers } 1064d703b5cSMark Powers if (v.b & 1ULL) { 1074d703b5cSMark Powers v.b = (v.a << 63)|(v.b >> 1); 1084d703b5cSMark Powers v.a = (v.a >> 1) ^ R; 1094d703b5cSMark Powers } else { 1104d703b5cSMark Powers v.b = (v.a << 63)|(v.b >> 1); 1114d703b5cSMark Powers v.a = v.a >> 1; 1124d703b5cSMark Powers } 1134d703b5cSMark Powers } 1144d703b5cSMark Powers } 1154d703b5cSMark Powers res[0] = htonll(z.a); 1164d703b5cSMark Powers res[1] = htonll(z.b); 1174d703b5cSMark Powers } 118*104d3bdeSDan OpenSolaris Anderson } 119*104d3bdeSDan OpenSolaris Anderson 1204d703b5cSMark Powers 1214d703b5cSMark Powers #define GHASH(c, d, t) \ 1224d703b5cSMark Powers xor_block((uint8_t *)(d), (uint8_t *)(c)->gcm_ghash); \ 1234d703b5cSMark Powers gcm_mul((uint64_t *)(c)->gcm_ghash, (c)->gcm_H, (uint64_t *)(t)); 1244d703b5cSMark Powers 1254d703b5cSMark Powers /* 1264d703b5cSMark Powers * Encrypt multiple blocks of data in GCM mode. Decrypt for GCM mode 1274d703b5cSMark Powers * is done in another function. 1284d703b5cSMark Powers */ 1294d703b5cSMark Powers int 1304d703b5cSMark Powers gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 1314d703b5cSMark Powers crypto_data_t *out, size_t block_size, 1324d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 1334d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 1344d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 1354d703b5cSMark Powers { 1364d703b5cSMark Powers size_t remainder = length; 1374d703b5cSMark Powers size_t need; 1384d703b5cSMark Powers uint8_t *datap = (uint8_t *)data; 1394d703b5cSMark Powers uint8_t *blockp; 1404d703b5cSMark Powers uint8_t *lastp; 1414d703b5cSMark Powers void *iov_or_mp; 1424d703b5cSMark Powers offset_t offset; 1434d703b5cSMark Powers uint8_t *out_data_1; 1444d703b5cSMark Powers uint8_t *out_data_2; 1454d703b5cSMark Powers size_t out_data_1_len; 1464d703b5cSMark Powers uint64_t counter; 1474d703b5cSMark Powers uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 1484d703b5cSMark Powers 1494d703b5cSMark Powers if (length + ctx->gcm_remainder_len < block_size) { 1504d703b5cSMark Powers /* accumulate bytes here and return */ 1514d703b5cSMark Powers bcopy(datap, 1524d703b5cSMark Powers (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len, 1534d703b5cSMark Powers length); 1544d703b5cSMark Powers ctx->gcm_remainder_len += length; 1554d703b5cSMark Powers ctx->gcm_copy_to = datap; 1564d703b5cSMark Powers return (CRYPTO_SUCCESS); 1574d703b5cSMark Powers } 1584d703b5cSMark Powers 1594d703b5cSMark Powers lastp = (uint8_t *)ctx->gcm_cb; 1604d703b5cSMark Powers if (out != NULL) 1614d703b5cSMark Powers crypto_init_ptrs(out, &iov_or_mp, &offset); 1624d703b5cSMark Powers 1634d703b5cSMark Powers do { 1644d703b5cSMark Powers /* Unprocessed data from last call. */ 1654d703b5cSMark Powers if (ctx->gcm_remainder_len > 0) { 1664d703b5cSMark Powers need = block_size - ctx->gcm_remainder_len; 1674d703b5cSMark Powers 1684d703b5cSMark Powers if (need > remainder) 1694d703b5cSMark Powers return (CRYPTO_DATA_LEN_RANGE); 1704d703b5cSMark Powers 1714d703b5cSMark Powers bcopy(datap, &((uint8_t *)ctx->gcm_remainder) 1724d703b5cSMark Powers [ctx->gcm_remainder_len], need); 1734d703b5cSMark Powers 1744d703b5cSMark Powers blockp = (uint8_t *)ctx->gcm_remainder; 1754d703b5cSMark Powers } else { 1764d703b5cSMark Powers blockp = datap; 1774d703b5cSMark Powers } 1784d703b5cSMark Powers 1794d703b5cSMark Powers /* 1804d703b5cSMark Powers * Increment counter. Counter bits are confined 1814d703b5cSMark Powers * to the bottom 32 bits of the counter block. 1824d703b5cSMark Powers */ 1834d703b5cSMark Powers counter = ntohll(ctx->gcm_cb[1] & counter_mask); 1844d703b5cSMark Powers counter = htonll(counter + 1); 1854d703b5cSMark Powers counter &= counter_mask; 1864d703b5cSMark Powers ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 1874d703b5cSMark Powers 1884d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 1894d703b5cSMark Powers (uint8_t *)ctx->gcm_tmp); 1904d703b5cSMark Powers xor_block(blockp, (uint8_t *)ctx->gcm_tmp); 1914d703b5cSMark Powers 1924d703b5cSMark Powers lastp = (uint8_t *)ctx->gcm_tmp; 1934d703b5cSMark Powers 1944d703b5cSMark Powers ctx->gcm_processed_data_len += block_size; 1954d703b5cSMark Powers 1964d703b5cSMark Powers if (out == NULL) { 1974d703b5cSMark Powers if (ctx->gcm_remainder_len > 0) { 1984d703b5cSMark Powers bcopy(blockp, ctx->gcm_copy_to, 1994d703b5cSMark Powers ctx->gcm_remainder_len); 2004d703b5cSMark Powers bcopy(blockp + ctx->gcm_remainder_len, datap, 2014d703b5cSMark Powers need); 2024d703b5cSMark Powers } 2034d703b5cSMark Powers } else { 2044d703b5cSMark Powers crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 2054d703b5cSMark Powers &out_data_1_len, &out_data_2, block_size); 2064d703b5cSMark Powers 2074d703b5cSMark Powers /* copy block to where it belongs */ 2084d703b5cSMark Powers if (out_data_1_len == block_size) { 2094d703b5cSMark Powers copy_block(lastp, out_data_1); 2104d703b5cSMark Powers } else { 2114d703b5cSMark Powers bcopy(lastp, out_data_1, out_data_1_len); 2124d703b5cSMark Powers if (out_data_2 != NULL) { 2134d703b5cSMark Powers bcopy(lastp + out_data_1_len, 2144d703b5cSMark Powers out_data_2, 2154d703b5cSMark Powers block_size - out_data_1_len); 2164d703b5cSMark Powers } 2174d703b5cSMark Powers } 2184d703b5cSMark Powers /* update offset */ 2194d703b5cSMark Powers out->cd_offset += block_size; 2204d703b5cSMark Powers } 2214d703b5cSMark Powers 2224d703b5cSMark Powers /* add ciphertext to the hash */ 2234d703b5cSMark Powers GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 2244d703b5cSMark Powers 2254d703b5cSMark Powers /* Update pointer to next block of data to be processed. */ 2264d703b5cSMark Powers if (ctx->gcm_remainder_len != 0) { 2274d703b5cSMark Powers datap += need; 2284d703b5cSMark Powers ctx->gcm_remainder_len = 0; 2294d703b5cSMark Powers } else { 2304d703b5cSMark Powers datap += block_size; 2314d703b5cSMark Powers } 2324d703b5cSMark Powers 2334d703b5cSMark Powers remainder = (size_t)&data[length] - (size_t)datap; 2344d703b5cSMark Powers 2354d703b5cSMark Powers /* Incomplete last block. */ 2364d703b5cSMark Powers if (remainder > 0 && remainder < block_size) { 2374d703b5cSMark Powers bcopy(datap, ctx->gcm_remainder, remainder); 2384d703b5cSMark Powers ctx->gcm_remainder_len = remainder; 2394d703b5cSMark Powers ctx->gcm_copy_to = datap; 2404d703b5cSMark Powers goto out; 2414d703b5cSMark Powers } 2424d703b5cSMark Powers ctx->gcm_copy_to = NULL; 2434d703b5cSMark Powers 2444d703b5cSMark Powers } while (remainder > 0); 2454d703b5cSMark Powers out: 2464d703b5cSMark Powers return (CRYPTO_SUCCESS); 2474d703b5cSMark Powers } 2484d703b5cSMark Powers 2494d703b5cSMark Powers /* ARGSUSED */ 2504d703b5cSMark Powers int 2514d703b5cSMark Powers gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 2524d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 2534d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 2544d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 2554d703b5cSMark Powers { 2564d703b5cSMark Powers uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 2574d703b5cSMark Powers uint8_t *ghash, *macp; 2584d703b5cSMark Powers int i, rv; 2594d703b5cSMark Powers 2604d703b5cSMark Powers if (out->cd_length < 2614d703b5cSMark Powers (ctx->gcm_remainder_len + ctx->gcm_tag_len)) { 2624d703b5cSMark Powers return (CRYPTO_DATA_LEN_RANGE); 2634d703b5cSMark Powers } 2644d703b5cSMark Powers 2654d703b5cSMark Powers ghash = (uint8_t *)ctx->gcm_ghash; 2664d703b5cSMark Powers 2674d703b5cSMark Powers if (ctx->gcm_remainder_len > 0) { 2684d703b5cSMark Powers uint64_t counter; 2694d703b5cSMark Powers uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp; 2704d703b5cSMark Powers 2714d703b5cSMark Powers /* 2724d703b5cSMark Powers * Here is where we deal with data that is not a 2734d703b5cSMark Powers * multiple of the block size. 2744d703b5cSMark Powers */ 2754d703b5cSMark Powers 2764d703b5cSMark Powers /* 2774d703b5cSMark Powers * Increment counter. 2784d703b5cSMark Powers */ 2794d703b5cSMark Powers counter = ntohll(ctx->gcm_cb[1] & counter_mask); 2804d703b5cSMark Powers counter = htonll(counter + 1); 2814d703b5cSMark Powers counter &= counter_mask; 2824d703b5cSMark Powers ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 2834d703b5cSMark Powers 2844d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 2854d703b5cSMark Powers (uint8_t *)ctx->gcm_tmp); 2864d703b5cSMark Powers 2874d703b5cSMark Powers macp = (uint8_t *)ctx->gcm_remainder; 2884d703b5cSMark Powers bzero(macp + ctx->gcm_remainder_len, 2894d703b5cSMark Powers block_size - ctx->gcm_remainder_len); 2904d703b5cSMark Powers 2914d703b5cSMark Powers /* XOR with counter block */ 2924d703b5cSMark Powers for (i = 0; i < ctx->gcm_remainder_len; i++) { 2934d703b5cSMark Powers macp[i] ^= tmpp[i]; 2944d703b5cSMark Powers } 2954d703b5cSMark Powers 2964d703b5cSMark Powers /* add ciphertext to the hash */ 2974d703b5cSMark Powers GHASH(ctx, macp, ghash); 2984d703b5cSMark Powers 2994d703b5cSMark Powers ctx->gcm_processed_data_len += ctx->gcm_remainder_len; 3004d703b5cSMark Powers } 3014d703b5cSMark Powers 3024d703b5cSMark Powers ctx->gcm_len_a_len_c[1] = htonll(ctx->gcm_processed_data_len << 3); 3034d703b5cSMark Powers GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 3044d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 3054d703b5cSMark Powers (uint8_t *)ctx->gcm_J0); 3064d703b5cSMark Powers xor_block((uint8_t *)ctx->gcm_J0, ghash); 3074d703b5cSMark Powers 3084d703b5cSMark Powers if (ctx->gcm_remainder_len > 0) { 3094d703b5cSMark Powers rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len); 3104d703b5cSMark Powers if (rv != CRYPTO_SUCCESS) 3114d703b5cSMark Powers return (rv); 3124d703b5cSMark Powers } 3134d703b5cSMark Powers out->cd_offset += ctx->gcm_remainder_len; 3144d703b5cSMark Powers ctx->gcm_remainder_len = 0; 3154d703b5cSMark Powers rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len); 3164d703b5cSMark Powers if (rv != CRYPTO_SUCCESS) 3174d703b5cSMark Powers return (rv); 3184d703b5cSMark Powers out->cd_offset += ctx->gcm_tag_len; 3194d703b5cSMark Powers 3204d703b5cSMark Powers return (CRYPTO_SUCCESS); 3214d703b5cSMark Powers } 3224d703b5cSMark Powers 3234d703b5cSMark Powers /* 3244d703b5cSMark Powers * This will only deal with decrypting the last block of the input that 3254d703b5cSMark Powers * might not be a multiple of block length. 3264d703b5cSMark Powers */ 3274d703b5cSMark Powers static void 3284d703b5cSMark Powers gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index, 3294d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3304d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 3314d703b5cSMark Powers { 3324d703b5cSMark Powers uint8_t *datap, *outp, *counterp; 3334d703b5cSMark Powers uint64_t counter; 3344d703b5cSMark Powers uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 3354d703b5cSMark Powers int i; 3364d703b5cSMark Powers 3374d703b5cSMark Powers /* 3384d703b5cSMark Powers * Increment counter. 3394d703b5cSMark Powers * Counter bits are confined to the bottom 32 bits 3404d703b5cSMark Powers */ 3414d703b5cSMark Powers counter = ntohll(ctx->gcm_cb[1] & counter_mask); 3424d703b5cSMark Powers counter = htonll(counter + 1); 3434d703b5cSMark Powers counter &= counter_mask; 3444d703b5cSMark Powers ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 3454d703b5cSMark Powers 3464d703b5cSMark Powers datap = (uint8_t *)ctx->gcm_remainder; 3474d703b5cSMark Powers outp = &((ctx->gcm_pt_buf)[index]); 3484d703b5cSMark Powers counterp = (uint8_t *)ctx->gcm_tmp; 3494d703b5cSMark Powers 3504d703b5cSMark Powers /* authentication tag */ 3514d703b5cSMark Powers bzero((uint8_t *)ctx->gcm_tmp, block_size); 3524d703b5cSMark Powers bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len); 3534d703b5cSMark Powers 3544d703b5cSMark Powers /* add ciphertext to the hash */ 3554d703b5cSMark Powers GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 3564d703b5cSMark Powers 3574d703b5cSMark Powers /* decrypt remaining ciphertext */ 3584d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp); 3594d703b5cSMark Powers 3604d703b5cSMark Powers /* XOR with counter block */ 3614d703b5cSMark Powers for (i = 0; i < ctx->gcm_remainder_len; i++) { 3624d703b5cSMark Powers outp[i] = datap[i] ^ counterp[i]; 3634d703b5cSMark Powers } 3644d703b5cSMark Powers } 3654d703b5cSMark Powers 3664d703b5cSMark Powers /* ARGSUSED */ 3674d703b5cSMark Powers int 3684d703b5cSMark Powers gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 3694d703b5cSMark Powers crypto_data_t *out, size_t block_size, 3704d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3714d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 3724d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 3734d703b5cSMark Powers { 3744d703b5cSMark Powers size_t new_len; 3754d703b5cSMark Powers uint8_t *new; 3764d703b5cSMark Powers 3774d703b5cSMark Powers /* 3784d703b5cSMark Powers * Copy contiguous ciphertext input blocks to plaintext buffer. 3794d703b5cSMark Powers * Ciphertext will be decrypted in the final. 3804d703b5cSMark Powers */ 3814d703b5cSMark Powers if (length > 0) { 3824d703b5cSMark Powers new_len = ctx->gcm_pt_buf_len + length; 3834d703b5cSMark Powers #ifdef _KERNEL 3844d703b5cSMark Powers new = kmem_alloc(new_len, ctx->gcm_kmflag); 3854d703b5cSMark Powers bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3864d703b5cSMark Powers kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); 3874d703b5cSMark Powers #else 3884d703b5cSMark Powers new = malloc(new_len); 3894d703b5cSMark Powers bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3904d703b5cSMark Powers free(ctx->gcm_pt_buf); 3914d703b5cSMark Powers #endif 3924d703b5cSMark Powers if (new == NULL) 3934d703b5cSMark Powers return (CRYPTO_HOST_MEMORY); 3944d703b5cSMark Powers 3954d703b5cSMark Powers ctx->gcm_pt_buf = new; 3964d703b5cSMark Powers ctx->gcm_pt_buf_len = new_len; 3974d703b5cSMark Powers bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len], 3984d703b5cSMark Powers length); 3994d703b5cSMark Powers ctx->gcm_processed_data_len += length; 4004d703b5cSMark Powers } 4014d703b5cSMark Powers 4024d703b5cSMark Powers ctx->gcm_remainder_len = 0; 4034d703b5cSMark Powers return (CRYPTO_SUCCESS); 4044d703b5cSMark Powers } 4054d703b5cSMark Powers 4064d703b5cSMark Powers int 4074d703b5cSMark Powers gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 4084d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 4094d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 4104d703b5cSMark Powers { 4114d703b5cSMark Powers size_t pt_len; 4124d703b5cSMark Powers size_t remainder; 4134d703b5cSMark Powers uint8_t *ghash; 4144d703b5cSMark Powers uint8_t *blockp; 4154d703b5cSMark Powers uint8_t *cbp; 4164d703b5cSMark Powers uint64_t counter; 4174d703b5cSMark Powers uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 4184d703b5cSMark Powers int processed = 0, rv; 4194d703b5cSMark Powers 4204d703b5cSMark Powers ASSERT(ctx->gcm_processed_data_len == ctx->gcm_pt_buf_len); 4214d703b5cSMark Powers 4224d703b5cSMark Powers pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len; 4234d703b5cSMark Powers ghash = (uint8_t *)ctx->gcm_ghash; 4244d703b5cSMark Powers blockp = ctx->gcm_pt_buf; 4254d703b5cSMark Powers remainder = pt_len; 4264d703b5cSMark Powers while (remainder > 0) { 4274d703b5cSMark Powers /* add ciphertext to the hash */ 4284d703b5cSMark Powers GHASH(ctx, blockp, ghash); 4294d703b5cSMark Powers 4304d703b5cSMark Powers /* 4314d703b5cSMark Powers * Increment counter. 4324d703b5cSMark Powers * Counter bits are confined to the bottom 32 bits 4334d703b5cSMark Powers */ 4344d703b5cSMark Powers counter = ntohll(ctx->gcm_cb[1] & counter_mask); 4354d703b5cSMark Powers counter = htonll(counter + 1); 4364d703b5cSMark Powers counter &= counter_mask; 4374d703b5cSMark Powers ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 4384d703b5cSMark Powers 4394d703b5cSMark Powers cbp = (uint8_t *)ctx->gcm_tmp; 4404d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp); 4414d703b5cSMark Powers 4424d703b5cSMark Powers /* XOR with ciphertext */ 4434d703b5cSMark Powers xor_block(cbp, blockp); 4444d703b5cSMark Powers 4454d703b5cSMark Powers processed += block_size; 4464d703b5cSMark Powers blockp += block_size; 4474d703b5cSMark Powers remainder -= block_size; 4484d703b5cSMark Powers 4494d703b5cSMark Powers /* Incomplete last block */ 4504d703b5cSMark Powers if (remainder > 0 && remainder < block_size) { 4514d703b5cSMark Powers bcopy(blockp, ctx->gcm_remainder, remainder); 4524d703b5cSMark Powers ctx->gcm_remainder_len = remainder; 4534d703b5cSMark Powers /* 4544d703b5cSMark Powers * not expecting anymore ciphertext, just 4554d703b5cSMark Powers * compute plaintext for the remaining input 4564d703b5cSMark Powers */ 4574d703b5cSMark Powers gcm_decrypt_incomplete_block(ctx, block_size, 4584d703b5cSMark Powers processed, encrypt_block, xor_block); 4594d703b5cSMark Powers ctx->gcm_remainder_len = 0; 4604d703b5cSMark Powers goto out; 4614d703b5cSMark Powers } 4624d703b5cSMark Powers } 4634d703b5cSMark Powers out: 4644d703b5cSMark Powers ctx->gcm_len_a_len_c[1] = htonll(pt_len << 3); 4654d703b5cSMark Powers GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 4664d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 4674d703b5cSMark Powers (uint8_t *)ctx->gcm_J0); 4684d703b5cSMark Powers xor_block((uint8_t *)ctx->gcm_J0, ghash); 4694d703b5cSMark Powers 4704d703b5cSMark Powers /* compare the input authentication tag with what we calculated */ 4714d703b5cSMark Powers if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) { 4724d703b5cSMark Powers /* They don't match */ 4734d703b5cSMark Powers return (CRYPTO_INVALID_MAC); 4744d703b5cSMark Powers } else { 4754d703b5cSMark Powers rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len); 4764d703b5cSMark Powers if (rv != CRYPTO_SUCCESS) 4774d703b5cSMark Powers return (rv); 4784d703b5cSMark Powers out->cd_offset += pt_len; 4794d703b5cSMark Powers } 4804d703b5cSMark Powers return (CRYPTO_SUCCESS); 4814d703b5cSMark Powers } 4824d703b5cSMark Powers 4834d703b5cSMark Powers static int 4844d703b5cSMark Powers gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param) 4854d703b5cSMark Powers { 4864d703b5cSMark Powers size_t tag_len; 4874d703b5cSMark Powers 4884d703b5cSMark Powers /* 4894d703b5cSMark Powers * Check the length of the authentication tag (in bits). 4904d703b5cSMark Powers */ 4914d703b5cSMark Powers tag_len = gcm_param->ulTagBits; 4924d703b5cSMark Powers switch (tag_len) { 4934d703b5cSMark Powers case 32: 4944d703b5cSMark Powers case 64: 4954d703b5cSMark Powers case 96: 4964d703b5cSMark Powers case 104: 4974d703b5cSMark Powers case 112: 4984d703b5cSMark Powers case 120: 4994d703b5cSMark Powers case 128: 5004d703b5cSMark Powers break; 5014d703b5cSMark Powers default: 5024d703b5cSMark Powers return (CRYPTO_MECHANISM_PARAM_INVALID); 5034d703b5cSMark Powers } 5044d703b5cSMark Powers 5054d703b5cSMark Powers if (gcm_param->ulIvLen == 0) 5064d703b5cSMark Powers return (CRYPTO_MECHANISM_PARAM_INVALID); 5074d703b5cSMark Powers 5084d703b5cSMark Powers return (CRYPTO_SUCCESS); 5094d703b5cSMark Powers } 5104d703b5cSMark Powers 5114d703b5cSMark Powers static void 5124d703b5cSMark Powers gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len, 5134d703b5cSMark Powers gcm_ctx_t *ctx, size_t block_size, 5144d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 5154d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 5164d703b5cSMark Powers { 5174d703b5cSMark Powers uint8_t *cb; 5184d703b5cSMark Powers ulong_t remainder = iv_len; 5194d703b5cSMark Powers ulong_t processed = 0; 5204d703b5cSMark Powers uint8_t *datap, *ghash; 5214d703b5cSMark Powers uint64_t len_a_len_c[2]; 5224d703b5cSMark Powers 5234d703b5cSMark Powers ghash = (uint8_t *)ctx->gcm_ghash; 5244d703b5cSMark Powers cb = (uint8_t *)ctx->gcm_cb; 5254d703b5cSMark Powers if (iv_len == 12) { 5264d703b5cSMark Powers bcopy(iv, cb, 12); 5274d703b5cSMark Powers cb[12] = 0; 5284d703b5cSMark Powers cb[13] = 0; 5294d703b5cSMark Powers cb[14] = 0; 5304d703b5cSMark Powers cb[15] = 1; 5314d703b5cSMark Powers /* J0 will be used again in the final */ 5324d703b5cSMark Powers copy_block(cb, (uint8_t *)ctx->gcm_J0); 5334d703b5cSMark Powers } else { 5344d703b5cSMark Powers /* GHASH the IV */ 5354d703b5cSMark Powers do { 5364d703b5cSMark Powers if (remainder < block_size) { 5374d703b5cSMark Powers bzero(cb, block_size); 5384d703b5cSMark Powers bcopy(&(iv[processed]), cb, remainder); 5394d703b5cSMark Powers datap = (uint8_t *)cb; 5404d703b5cSMark Powers remainder = 0; 5414d703b5cSMark Powers } else { 5424d703b5cSMark Powers datap = (uint8_t *)(&(iv[processed])); 5434d703b5cSMark Powers processed += block_size; 5444d703b5cSMark Powers remainder -= block_size; 5454d703b5cSMark Powers } 5464d703b5cSMark Powers GHASH(ctx, datap, ghash); 5474d703b5cSMark Powers } while (remainder > 0); 5484d703b5cSMark Powers 5494d703b5cSMark Powers len_a_len_c[0] = 0; 5504d703b5cSMark Powers len_a_len_c[1] = htonll(iv_len << 3); 5514d703b5cSMark Powers GHASH(ctx, len_a_len_c, ctx->gcm_J0); 5524d703b5cSMark Powers 5534d703b5cSMark Powers /* J0 will be used again in the final */ 5544d703b5cSMark Powers copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb); 5554d703b5cSMark Powers } 5564d703b5cSMark Powers } 5574d703b5cSMark Powers 5584d703b5cSMark Powers /* 5594d703b5cSMark Powers * The following function is called at encrypt or decrypt init time 5604d703b5cSMark Powers * for AES GCM mode. 5614d703b5cSMark Powers */ 5624d703b5cSMark Powers int 5634d703b5cSMark Powers gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len, 5644d703b5cSMark Powers unsigned char *auth_data, size_t auth_data_len, size_t block_size, 5654d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 5664d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 5674d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 5684d703b5cSMark Powers { 5694d703b5cSMark Powers uint8_t *ghash, *datap, *authp; 5704d703b5cSMark Powers size_t remainder, processed; 5714d703b5cSMark Powers 5724d703b5cSMark Powers /* encrypt zero block to get subkey H */ 5734d703b5cSMark Powers bzero(ctx->gcm_H, sizeof (ctx->gcm_H)); 5744d703b5cSMark Powers encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H, 5754d703b5cSMark Powers (uint8_t *)ctx->gcm_H); 5764d703b5cSMark Powers 5774d703b5cSMark Powers gcm_format_initial_blocks(iv, iv_len, ctx, block_size, 5784d703b5cSMark Powers copy_block, xor_block); 5794d703b5cSMark Powers 5804d703b5cSMark Powers authp = (uint8_t *)ctx->gcm_tmp; 5814d703b5cSMark Powers ghash = (uint8_t *)ctx->gcm_ghash; 5824d703b5cSMark Powers bzero(authp, block_size); 5834d703b5cSMark Powers bzero(ghash, block_size); 5844d703b5cSMark Powers 5854d703b5cSMark Powers processed = 0; 5864d703b5cSMark Powers remainder = auth_data_len; 5874d703b5cSMark Powers do { 5884d703b5cSMark Powers if (remainder < block_size) { 5894d703b5cSMark Powers /* 5904d703b5cSMark Powers * There's not a block full of data, pad rest of 5914d703b5cSMark Powers * buffer with zero 5924d703b5cSMark Powers */ 5934d703b5cSMark Powers bzero(authp, block_size); 5944d703b5cSMark Powers bcopy(&(auth_data[processed]), authp, remainder); 5954d703b5cSMark Powers datap = (uint8_t *)authp; 5964d703b5cSMark Powers remainder = 0; 5974d703b5cSMark Powers } else { 5984d703b5cSMark Powers datap = (uint8_t *)(&(auth_data[processed])); 5994d703b5cSMark Powers processed += block_size; 6004d703b5cSMark Powers remainder -= block_size; 6014d703b5cSMark Powers } 6024d703b5cSMark Powers 6034d703b5cSMark Powers /* add auth data to the hash */ 6044d703b5cSMark Powers GHASH(ctx, datap, ghash); 6054d703b5cSMark Powers 6064d703b5cSMark Powers } while (remainder > 0); 6074d703b5cSMark Powers 6084d703b5cSMark Powers return (CRYPTO_SUCCESS); 6094d703b5cSMark Powers } 6104d703b5cSMark Powers 6114d703b5cSMark Powers int 6124d703b5cSMark Powers gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 6134d703b5cSMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 6144d703b5cSMark Powers void (*copy_block)(uint8_t *, uint8_t *), 6154d703b5cSMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 6164d703b5cSMark Powers { 6174d703b5cSMark Powers int rv; 6184d703b5cSMark Powers CK_AES_GCM_PARAMS *gcm_param; 6194d703b5cSMark Powers 6204d703b5cSMark Powers if (param != NULL) { 6214d703b5cSMark Powers gcm_param = (CK_AES_GCM_PARAMS *)param; 6224d703b5cSMark Powers 6234d703b5cSMark Powers if ((rv = gcm_validate_args(gcm_param)) != 0) { 6244d703b5cSMark Powers return (rv); 6254d703b5cSMark Powers } 6264d703b5cSMark Powers 6274d703b5cSMark Powers gcm_ctx->gcm_tag_len = gcm_param->ulTagBits; 6284d703b5cSMark Powers gcm_ctx->gcm_tag_len >>= 3; 6294d703b5cSMark Powers gcm_ctx->gcm_processed_data_len = 0; 6304d703b5cSMark Powers 6314d703b5cSMark Powers /* these values are in bits */ 6324d703b5cSMark Powers gcm_ctx->gcm_len_a_len_c[0] = htonll(gcm_param->ulAADLen << 3); 6334d703b5cSMark Powers 6344d703b5cSMark Powers rv = CRYPTO_SUCCESS; 6354d703b5cSMark Powers gcm_ctx->gcm_flags |= GCM_MODE; 6364d703b5cSMark Powers } else { 6374d703b5cSMark Powers rv = CRYPTO_MECHANISM_PARAM_INVALID; 6384d703b5cSMark Powers goto out; 6394d703b5cSMark Powers } 6404d703b5cSMark Powers 6414d703b5cSMark Powers if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen, 6424d703b5cSMark Powers gcm_param->pAAD, gcm_param->ulAADLen, block_size, 6434d703b5cSMark Powers encrypt_block, copy_block, xor_block) != 0) { 6444d703b5cSMark Powers rv = CRYPTO_MECHANISM_PARAM_INVALID; 6454d703b5cSMark Powers } 6464d703b5cSMark Powers out: 6474d703b5cSMark Powers return (rv); 6484d703b5cSMark Powers } 6494d703b5cSMark Powers 650983a1033SMark Powers int 651983a1033SMark Powers gmac_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 652983a1033SMark Powers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 653983a1033SMark Powers void (*copy_block)(uint8_t *, uint8_t *), 654983a1033SMark Powers void (*xor_block)(uint8_t *, uint8_t *)) 655983a1033SMark Powers { 656983a1033SMark Powers int rv; 657983a1033SMark Powers CK_AES_GMAC_PARAMS *gmac_param; 658983a1033SMark Powers 659983a1033SMark Powers if (param != NULL) { 660983a1033SMark Powers gmac_param = (CK_AES_GMAC_PARAMS *)param; 661983a1033SMark Powers 662983a1033SMark Powers gcm_ctx->gcm_tag_len = CRYPTO_BITS2BYTES(AES_GMAC_TAG_BITS); 663983a1033SMark Powers gcm_ctx->gcm_processed_data_len = 0; 664983a1033SMark Powers 665983a1033SMark Powers /* these values are in bits */ 666983a1033SMark Powers gcm_ctx->gcm_len_a_len_c[0] = htonll(gmac_param->ulAADLen << 3); 667983a1033SMark Powers 668983a1033SMark Powers rv = CRYPTO_SUCCESS; 669983a1033SMark Powers gcm_ctx->gcm_flags |= GMAC_MODE; 670983a1033SMark Powers } else { 671983a1033SMark Powers rv = CRYPTO_MECHANISM_PARAM_INVALID; 672983a1033SMark Powers goto out; 673983a1033SMark Powers } 674983a1033SMark Powers 675983a1033SMark Powers if (gcm_init(gcm_ctx, gmac_param->pIv, AES_GMAC_IV_LEN, 676983a1033SMark Powers gmac_param->pAAD, gmac_param->ulAADLen, block_size, 677983a1033SMark Powers encrypt_block, copy_block, xor_block) != 0) { 678983a1033SMark Powers rv = CRYPTO_MECHANISM_PARAM_INVALID; 679983a1033SMark Powers } 680983a1033SMark Powers out: 681983a1033SMark Powers return (rv); 682983a1033SMark Powers } 683983a1033SMark Powers 6844d703b5cSMark Powers void * 6854d703b5cSMark Powers gcm_alloc_ctx(int kmflag) 6864d703b5cSMark Powers { 6874d703b5cSMark Powers gcm_ctx_t *gcm_ctx; 6884d703b5cSMark Powers 6894d703b5cSMark Powers #ifdef _KERNEL 6904d703b5cSMark Powers if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 6914d703b5cSMark Powers #else 6924d703b5cSMark Powers if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 6934d703b5cSMark Powers #endif 6944d703b5cSMark Powers return (NULL); 6954d703b5cSMark Powers 6964d703b5cSMark Powers gcm_ctx->gcm_flags = GCM_MODE; 6974d703b5cSMark Powers return (gcm_ctx); 6984d703b5cSMark Powers } 6994d703b5cSMark Powers 700983a1033SMark Powers void * 701983a1033SMark Powers gmac_alloc_ctx(int kmflag) 702983a1033SMark Powers { 703983a1033SMark Powers gcm_ctx_t *gcm_ctx; 704983a1033SMark Powers 705983a1033SMark Powers #ifdef _KERNEL 706983a1033SMark Powers if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 707983a1033SMark Powers #else 708983a1033SMark Powers if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 709983a1033SMark Powers #endif 710983a1033SMark Powers return (NULL); 711983a1033SMark Powers 712983a1033SMark Powers gcm_ctx->gcm_flags = GMAC_MODE; 713983a1033SMark Powers return (gcm_ctx); 714983a1033SMark Powers } 715983a1033SMark Powers 7164d703b5cSMark Powers void 7174d703b5cSMark Powers gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag) 7184d703b5cSMark Powers { 7194d703b5cSMark Powers ctx->gcm_kmflag = kmflag; 7204d703b5cSMark Powers } 721*104d3bdeSDan OpenSolaris Anderson 722*104d3bdeSDan OpenSolaris Anderson 723*104d3bdeSDan OpenSolaris Anderson #ifdef __amd64 724*104d3bdeSDan OpenSolaris Anderson /* 725*104d3bdeSDan OpenSolaris Anderson * Return 1 if executing on Intel with PCLMULQDQ instructions, 726*104d3bdeSDan OpenSolaris Anderson * otherwise 0 (i.e., Intel without PCLMULQDQ or AMD64). 727*104d3bdeSDan OpenSolaris Anderson * Cache the result, as the CPU can't change. 728*104d3bdeSDan OpenSolaris Anderson * 729*104d3bdeSDan OpenSolaris Anderson * Note: the userland version uses getisax(). The kernel version uses 730*104d3bdeSDan OpenSolaris Anderson * global variable x86_feature or the output of cpuid_insn(). 731*104d3bdeSDan OpenSolaris Anderson */ 732*104d3bdeSDan OpenSolaris Anderson static int 733*104d3bdeSDan OpenSolaris Anderson intel_pclmulqdq_instruction_present(void) 734*104d3bdeSDan OpenSolaris Anderson { 735*104d3bdeSDan OpenSolaris Anderson static int cached_result = -1; 736*104d3bdeSDan OpenSolaris Anderson 737*104d3bdeSDan OpenSolaris Anderson if (cached_result == -1) { /* first time */ 738*104d3bdeSDan OpenSolaris Anderson #ifdef _KERNEL 739*104d3bdeSDan OpenSolaris Anderson #ifdef X86_PCLMULQDQ 740*104d3bdeSDan OpenSolaris Anderson cached_result = (x86_feature & X86_PCLMULQDQ) != 0; 741*104d3bdeSDan OpenSolaris Anderson #else 742*104d3bdeSDan OpenSolaris Anderson if (cpuid_getvendor(CPU) == X86_VENDOR_Intel) { 743*104d3bdeSDan OpenSolaris Anderson struct cpuid_regs cpr; 744*104d3bdeSDan OpenSolaris Anderson cpu_t *cp = CPU; 745*104d3bdeSDan OpenSolaris Anderson 746*104d3bdeSDan OpenSolaris Anderson cpr.cp_eax = 1; /* Function 1: get processor info */ 747*104d3bdeSDan OpenSolaris Anderson (void) cpuid_insn(cp, &cpr); 748*104d3bdeSDan OpenSolaris Anderson cached_result = ((cpr.cp_ecx & 749*104d3bdeSDan OpenSolaris Anderson CPUID_INTC_ECX_PCLMULQDQ) != 0); 750*104d3bdeSDan OpenSolaris Anderson } else { 751*104d3bdeSDan OpenSolaris Anderson cached_result = 0; 752*104d3bdeSDan OpenSolaris Anderson } 753*104d3bdeSDan OpenSolaris Anderson #endif /* X86_PCLMULQDQ */ 754*104d3bdeSDan OpenSolaris Anderson #else 755*104d3bdeSDan OpenSolaris Anderson uint_t ui = 0; 756*104d3bdeSDan OpenSolaris Anderson 757*104d3bdeSDan OpenSolaris Anderson (void) getisax(&ui, 1); 758*104d3bdeSDan OpenSolaris Anderson cached_result = (ui & AV_386_PCLMULQDQ) != 0; 759*104d3bdeSDan OpenSolaris Anderson #endif /* _KERNEL */ 760*104d3bdeSDan OpenSolaris Anderson } 761*104d3bdeSDan OpenSolaris Anderson 762*104d3bdeSDan OpenSolaris Anderson return (cached_result); 763*104d3bdeSDan OpenSolaris Anderson } 764*104d3bdeSDan OpenSolaris Anderson #endif /* __amd64 */ 765