blob: 32d999c78710693b9261eed9ed29f2d31808f801 [file] [log] [blame]
/*
* Copyright(c) 2012-2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define pr_fmt(fmt) "sep_hwk: " fmt
#include <linux/module.h>
#include <linux/crypto.h>
#include <crypto/algapi.h>
#include <linux/highmem.h>
#include "dx_driver.h"
#include "sep_sysfs.h"
#include "sep_power.h"
#include "dx_sepapp_kapi.h"
#define HWK_APP_UUID "INTEL HWK 000001"
#define HWK_CMD_CRYPTO 8
struct hwk_context {
struct sep_client_ctx *sctx;
u32 sess_id, key_id;
};
static inline void hwk_pm_runtime_get(void)
{
#ifdef SEP_RUNTIME_PM
dx_sep_pm_runtime_get();
#endif
}
static inline void hwk_pm_runtime_put(void)
{
#ifdef SEP_RUNTIME_PM
dx_sep_pm_runtime_put();
#endif
}
static int hwk_ctx_init(struct crypto_tfm *tfm)
{
struct hwk_context *hctx = crypto_tfm_ctx(tfm);
u8 uuid[16] = HWK_APP_UUID;
enum dxdi_sep_module ret;
int rc;
hwk_pm_runtime_get();
hctx->sctx = dx_sepapp_context_alloc();
if (!hctx->sctx) {
rc = -ENOMEM;
goto out;
}
pr_debug("%s: opening session\n", __func__);
rc = dx_sepapp_session_open(hctx->sctx, uuid, 0, NULL, NULL,
&hctx->sess_id, &ret);
pr_debug("%s: %d: %p %d\n", __func__, rc, hctx->sctx, hctx->sess_id);
if (rc != 0)
dx_sepapp_context_free(hctx->sctx);
out:
hwk_pm_runtime_put();
return rc;
}
static void hwk_ctx_cleanup(struct crypto_tfm *tfm)
{
struct hwk_context *hctx = crypto_tfm_ctx(tfm);
pr_debug("%s: %p %d\n", __func__, hctx->sctx, hctx->sess_id);
if (dx_sepapp_session_close(hctx->sctx, hctx->sess_id))
BUG();
dx_sepapp_context_free(hctx->sctx);
pr_debug("%s: session closed\n", __func__);
}
static int hwk_set_key(struct crypto_ablkcipher *tfm,
const u8 *key, unsigned int keylen)
{
struct hwk_context *hctx = crypto_tfm_ctx(crypto_ablkcipher_tfm(tfm));
hctx->key_id = *((u32 *)key);
pr_debug("%s: key_id=%d\n", __func__, hctx->key_id);
return 0;
}
#if defined(HWK_ST_DUMP_BUF) || defined(HWK_DUMP_BUF)
static void hwk_dump_buf(u8 *buf, const char *buf_name, int len)
{
int i;
for (i = 0; i < len; i++) {
if (i % 64 == 0)
printk("\n%s: ", buf_name);
printk("%02x", buf[i]);
}
printk("\n");
}
#endif
#ifdef HWK_DUMP_BUF
static void hwk_dump_sg(struct scatterlist *sg, const char *buf_name)
{
u8 *buf = kmap(sg_page(sg));
hwk_dump_buf(buf + sg->offset, buf_name, sg->length);
kunmap(sg_page(sg));
}
#endif
static int hwk_process(struct ablkcipher_request *req, bool encrypt)
{
struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
struct hwk_context *hctx = crypto_tfm_ctx(crypto_ablkcipher_tfm(tfm));
struct dxdi_sepapp_kparams p;
enum dxdi_sep_module ret_origin;
struct scatterlist iv_sg;
struct page *iv_page;
int rc;
iv_page = virt_to_page(req->info);
sg_init_table(&iv_sg, 1);
sg_set_page(&iv_sg, iv_page, SEP_AES_IV_SIZE,
(unsigned long)req->info % PAGE_SIZE);
#ifdef HWK_DUMP_BUF
hwk_dump_buf(req->info, "iv", SEP_AES_IV_SIZE);
hwk_dump_sg(&iv_sg, "iv");
hwk_dump_sg(req->src, "src");
#endif
memset(&p, 0, sizeof(p));
p.params_types[0] = DXDI_SEPAPP_PARAM_VAL;
p.params[0].val.data[0] = hctx->key_id;
p.params[0].val.data[1] = req->nbytes | (encrypt << 16);
p.params[0].val.copy_dir = DXDI_DATA_TO_DEVICE;
p.params_types[1] = DXDI_SEPAPP_PARAM_MEMREF;
p.params[1].kmemref.dma_direction = DXDI_DATA_TO_DEVICE;
p.params[1].kmemref.sgl = &iv_sg;
p.params[1].kmemref.nbytes = SEP_AES_IV_SIZE;
p.params_types[2] = DXDI_SEPAPP_PARAM_MEMREF;
p.params[2].kmemref.dma_direction = DXDI_DATA_TO_DEVICE;
p.params[2].kmemref.sgl = req->src;
p.params[2].kmemref.nbytes = req->nbytes;
p.params_types[3] = DXDI_SEPAPP_PARAM_MEMREF;
p.params[3].kmemref.dma_direction = DXDI_DATA_FROM_DEVICE;
p.params[3].kmemref.sgl = req->dst;
p.params[3].kmemref.nbytes = req->nbytes;
pr_debug("%s: size=%d dir=%d\n", __func__, req->nbytes, encrypt);
rc = dx_sepapp_command_invoke(hctx->sctx, hctx->sess_id,
HWK_CMD_CRYPTO, &p, &ret_origin);
pr_debug("%s: done: %d\n", __func__, rc);
if (rc != 0) {
pr_err("%s: error invoking command %d: %x (ret_origin= %x)\n",
__func__, HWK_CMD_CRYPTO, rc, ret_origin);
return -EINVAL;
}
#ifdef HWK_DUMP_BUF
hwk_dump_sg(req->dst, "dst");
#endif
return rc;
}
static int hwk_encrypt(struct ablkcipher_request *req)
{
return hwk_process(req, true);
}
static int hwk_decrypt(struct ablkcipher_request *req)
{
return hwk_process(req, false);
}
static struct crypto_alg hwk_alg = {
.cra_priority = 0,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_ctxsize = sizeof(struct hwk_context),
.cra_alignmask = 0, /* Cannot use this due to bug in kernel */
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_name = "cbchk(aes)",
.cra_driver_name = MODULE_NAME "-aes-cbchk",
.cra_blocksize = SEP_AES_BLOCK_SIZE,
.cra_u = {
.ablkcipher = {
.min_keysize = SEP_AES_256_BIT_KEY_SIZE,
.max_keysize = SEP_AES_256_BIT_KEY_SIZE,
.ivsize = SEP_AES_IV_SIZE,
.setkey = hwk_set_key,
.encrypt = hwk_encrypt,
.decrypt = hwk_decrypt,
},
},
.cra_init = hwk_ctx_init,
.cra_exit = hwk_ctx_cleanup
};
int hwk_init(void)
{
int rc = crypto_register_alg(&hwk_alg);
if (rc != 0)
pr_err("failed to register %s\n", hwk_alg.cra_name);
return rc;
}
void hwk_fini(void)
{
crypto_unregister_alg(&hwk_alg);
}
#ifdef SEP_HWK_UNIT_TEST
enum hwk_self_test {
HWK_ST_NOT_STARTED = 0,
HWK_ST_RUNNING,
HWK_ST_SUCCESS,
HWK_ST_ERROR
};
static enum hwk_self_test hwk_st_status = HWK_ST_NOT_STARTED;
static const char * const hwk_st_strings[] = {
"not started",
"running",
"success",
"error"
};
ssize_t sys_hwk_st_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n", hwk_st_strings[hwk_st_status]);
}
struct hwk_st_op_result {
struct completion completion;
int rc;
};
static void hwk_st_op_complete(struct crypto_async_request *req, int rc)
{
struct hwk_st_op_result *hr = req->data;
if (rc == -EINPROGRESS)
return;
hr->rc = rc;
complete(&hr->completion);
}
static int hwk_st_do_op(struct ablkcipher_request *req, struct page *src,
struct page *dst, bool enc)
{
struct scatterlist src_sg, dst_sg;
struct hwk_st_op_result hr = { .rc = 0 };
char iv[SEP_AES_IV_SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15 };
int ret;
init_completion(&hr.completion);
ablkcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
hwk_st_op_complete, &hr);
sg_init_table(&src_sg, 1);
sg_set_page(&src_sg, src, PAGE_SIZE, 0);
sg_init_table(&dst_sg, 1);
sg_set_page(&dst_sg, dst, PAGE_SIZE, 0);
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, PAGE_SIZE, iv);
pr_info("%s: submiting %s op..\n", __func__, enc ? "enc" : "dec");
if (enc)
ret = crypto_ablkcipher_encrypt(req);
else
ret = crypto_ablkcipher_decrypt(req);
pr_info("%s: op submitted\n", __func__);
if (ret == -EINPROGRESS || ret == -EBUSY) {
wait_for_completion(&hr.completion);
ret = hr.rc;
}
pr_info("%s: op completed\n", __func__);
return ret;
}
ssize_t sys_hwk_st_start(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct ablkcipher_request *req;
struct page *src, *enc, *dec;
struct crypto_ablkcipher *acipher;
char *tmp, *tmp2, *tmp3;
int ret = -EINVAL, i;
u32 hwk_id;
if (hwk_st_status == HWK_ST_RUNNING)
return count;
hwk_st_status = HWK_ST_RUNNING;
ret = kstrtouint(buf, 10, &hwk_id);
if (ret) {
pr_err("bad hardware key id: %d\n", ret);
goto out;
}
ret = -ENOMEM;
src = alloc_page(GFP_KERNEL);
if (!src) {
pr_err("failed to allocate src page\n");
goto out;
}
enc = alloc_page(GFP_KERNEL);
if (!enc) {
pr_err("failed to allocate enc page\n");
goto out_free_src;
}
dec = alloc_page(GFP_KERNEL);
if (!dec) {
pr_err("failed to allocate dec page\n");
goto out_free_enc;
}
acipher = crypto_alloc_ablkcipher("cbchk(aes)", 0, 0);
if (IS_ERR(acipher)) {
pr_err("error allocating cipher: %ld\n", PTR_ERR(acipher));
ret = -EINVAL;
goto out_free_dec;
}
tmp = kmap(src);
for (i = 0; i < PAGE_SIZE; i++)
tmp[i] = i;
kunmap(src);
crypto_ablkcipher_set_flags(acipher, CRYPTO_TFM_REQ_WEAK_KEY);
pr_debug("setting hardware key %d\n", hwk_id);
ret = crypto_ablkcipher_setkey(acipher, (u8 *)&hwk_id, sizeof(hwk_id));
if (ret) {
pr_err("error setting hardware key: %d\n", ret);
goto out_free_cipher;
}
req = ablkcipher_request_alloc(acipher, GFP_NOFS);
if (!req) {
ret = -EINVAL;
pr_err("failed to allocate cipher request\n");
goto out_free_cipher;
}
ret = hwk_st_do_op(req, src, enc, true);
if (ret) {
pr_err("encryption failed: %d\n", ret);
goto out_free_req;
}
ret = hwk_st_do_op(req, enc, dec, false);
if (ret) {
pr_err("decryption failed: %d\n", ret);
goto out_free_req;
}
tmp = kmap(src); tmp2 = kmap(enc); tmp3 = kmap(dec);
#ifdef HWK_ST_DUMP_BUF
hwk_dump_buf(tmp, "src", PAGE_SIZE);
hwk_dump_buf(tmp2, "enc", PAGE_SIZE);
hwk_dump_buf(tmp3, "dec", PAGE_SIZE);
#endif
for (i = 0; i < PAGE_SIZE; i++) {
if (tmp[i] != tmp3[i]) {
ret = -EINVAL;
break;
}
}
kunmap(src); kunmap(enc); kunmap(dec);
if (ret)
pr_err("dec != src\n");
out_free_req:
ablkcipher_request_free(req);
out_free_cipher:
crypto_free_ablkcipher(acipher);
out_free_dec:
__free_pages(dec, 0);
out_free_enc:
__free_pages(enc, 0);
out_free_src:
__free_pages(src, 0);
out:
if (ret)
hwk_st_status = HWK_ST_ERROR;
else
hwk_st_status = HWK_ST_SUCCESS;
return count;
}
#endif