162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/hyperhold/hp_device.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "[HYPERHOLD]" fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/random.h>
1162306a36Sopenharmony_ci#include <linux/blk-crypto.h>
1262306a36Sopenharmony_ci#include <linux/scatterlist.h>
1362306a36Sopenharmony_ci#include <linux/version.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "hp_device.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define HP_CIPHER_MODE BLK_ENCRYPTION_MODE_AES_256_XTS
1862306a36Sopenharmony_ci#define HP_CIPHER_NAME "xts(aes)"
1962306a36Sopenharmony_ci#define HP_KEY_SIZE (64)
2062306a36Sopenharmony_ci#define HP_IV_SIZE (16)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciunion hp_iv {
2362306a36Sopenharmony_ci	__le64 index;
2462306a36Sopenharmony_ci	__le64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_civoid unbind_bdev(struct hp_device *dev)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	int ret;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (!dev->bdev)
3262306a36Sopenharmony_ci		goto close;
3362306a36Sopenharmony_ci	if (!dev->old_block_size)
3462306a36Sopenharmony_ci		goto put;
3562306a36Sopenharmony_ci	ret = set_blocksize(dev->bdev, dev->old_block_size);
3662306a36Sopenharmony_ci	if (ret)
3762306a36Sopenharmony_ci		pr_err("set old block size %d failed, err = %d!\n",
3862306a36Sopenharmony_ci				dev->old_block_size, ret);
3962306a36Sopenharmony_ci	dev->old_block_size = 0;
4062306a36Sopenharmony_ciput:
4162306a36Sopenharmony_ci	blkdev_put(dev->bdev, FMODE_READ | FMODE_WRITE);
4262306a36Sopenharmony_ci	dev->bdev = NULL;
4362306a36Sopenharmony_ciclose:
4462306a36Sopenharmony_ci	if (dev->filp)
4562306a36Sopenharmony_ci		filp_close(dev->filp, NULL);
4662306a36Sopenharmony_ci	dev->filp = NULL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	pr_info("hyperhold bdev unbinded.\n");
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cibool bind_bdev(struct hp_device *dev, const char *name)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct inode *inode = NULL;
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	dev->filp = filp_open(name, O_RDWR | O_LARGEFILE, 0);
5762306a36Sopenharmony_ci	if (IS_ERR(dev->filp)) {
5862306a36Sopenharmony_ci		pr_err("open file %s failed, err = %ld!\n", name, PTR_ERR(dev->filp));
5962306a36Sopenharmony_ci		dev->filp = NULL;
6062306a36Sopenharmony_ci		goto err;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	inode = dev->filp->f_mapping->host;
6362306a36Sopenharmony_ci	if (!S_ISBLK(inode->i_mode)) {
6462306a36Sopenharmony_ci		pr_err("%s is not a block device!\n", name);
6562306a36Sopenharmony_ci		goto err;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
6862306a36Sopenharmony_ci	dev->bdev = blkdev_get_by_dev(inode->i_rdev, BLK_OPEN_READ | BLK_OPEN_WRITE, dev, NULL);
6962306a36Sopenharmony_ci#else
7062306a36Sopenharmony_ci	dev->bdev = blkdev_get_by_dev(inode->i_rdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL, dev);
7162306a36Sopenharmony_ci#endif
7262306a36Sopenharmony_ci	if (IS_ERR(dev->bdev)) {
7362306a36Sopenharmony_ci		ret = PTR_ERR(dev->bdev);
7462306a36Sopenharmony_ci		dev->bdev = NULL;
7562306a36Sopenharmony_ci		pr_err("get blkdev %s failed, err = %d!\n", name, ret);
7662306a36Sopenharmony_ci		goto err;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	dev->old_block_size = block_size(dev->bdev);
7962306a36Sopenharmony_ci	ret = set_blocksize(dev->bdev, PAGE_SIZE);
8062306a36Sopenharmony_ci	if (ret) {
8162306a36Sopenharmony_ci		pr_err("set %s block size failed, err = %d!\n", name, ret);
8262306a36Sopenharmony_ci		goto err;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	dev->dev_size = (u64)i_size_read(inode);
8562306a36Sopenharmony_ci	dev->sec_size = SECTOR_SIZE;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	pr_info("hyperhold bind bdev %s of size %llu / %u succ.\n",
8862306a36Sopenharmony_ci			name, dev->dev_size, dev->sec_size);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return true;
9162306a36Sopenharmony_cierr:
9262306a36Sopenharmony_ci	unbind_bdev(dev);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return false;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint soft_crypt_page(struct crypto_skcipher *ctfm, struct page *dst_page,
9862306a36Sopenharmony_ci		    struct page *src_page, unsigned int op)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct skcipher_request *req = NULL;
10162306a36Sopenharmony_ci	DECLARE_CRYPTO_WAIT(wait);
10262306a36Sopenharmony_ci	struct scatterlist dst, src;
10362306a36Sopenharmony_ci	int ret = 0;
10462306a36Sopenharmony_ci	union hp_iv iv;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	memset(&iv, 0, sizeof(union hp_iv));
10762306a36Sopenharmony_ci	iv.index = cpu_to_le64(src_page->index);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	req = skcipher_request_alloc(ctfm, GFP_NOIO);
11062306a36Sopenharmony_ci	if (!req) {
11162306a36Sopenharmony_ci		pr_err("alloc skcipher request failed!\n");
11262306a36Sopenharmony_ci		return -ENOMEM;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
11662306a36Sopenharmony_ci			crypto_req_done, &wait);
11762306a36Sopenharmony_ci	sg_init_table(&dst, 1);
11862306a36Sopenharmony_ci	sg_set_page(&dst, dst_page, PAGE_SIZE, 0);
11962306a36Sopenharmony_ci	sg_init_table(&src, 1);
12062306a36Sopenharmony_ci	sg_set_page(&src, src_page, PAGE_SIZE, 0);
12162306a36Sopenharmony_ci	skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE, &iv);
12262306a36Sopenharmony_ci	if (op == HP_DEV_ENCRYPT)
12362306a36Sopenharmony_ci		ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
12462306a36Sopenharmony_ci	else if (op == HP_DEV_DECRYPT)
12562306a36Sopenharmony_ci		ret = crypto_wait_req(crypto_skcipher_decrypt(req), &wait);
12662306a36Sopenharmony_ci	else
12762306a36Sopenharmony_ci		BUG();
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	skcipher_request_free(req);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (ret)
13262306a36Sopenharmony_ci		pr_err("%scrypt failed!\n", op == HP_DEV_ENCRYPT ? "en" : "de");
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return ret;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic struct crypto_skcipher *soft_crypto_init(const u8 *key)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	char *cipher = HP_CIPHER_NAME;
14062306a36Sopenharmony_ci	u32 key_len = HP_KEY_SIZE;
14162306a36Sopenharmony_ci	struct crypto_skcipher *ctfm = NULL;
14262306a36Sopenharmony_ci	int ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ctfm = crypto_alloc_skcipher(cipher, 0, 0);
14562306a36Sopenharmony_ci	if (IS_ERR(ctfm)) {
14662306a36Sopenharmony_ci		pr_err("alloc ctfm failed, ret = %ld!\n", PTR_ERR(ctfm));
14762306a36Sopenharmony_ci		ctfm = NULL;
14862306a36Sopenharmony_ci		goto err;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	crypto_skcipher_clear_flags(ctfm, ~0);
15162306a36Sopenharmony_ci	crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
15262306a36Sopenharmony_ci	ret = crypto_skcipher_setkey(ctfm, key, key_len);
15362306a36Sopenharmony_ci	if (ret) {
15462306a36Sopenharmony_ci		pr_err("ctfm setkey failed, ret = %d!\n", ret);
15562306a36Sopenharmony_ci		goto err;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return ctfm;
15962306a36Sopenharmony_cierr:
16062306a36Sopenharmony_ci	if (ctfm)
16162306a36Sopenharmony_ci		crypto_free_skcipher(ctfm);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return NULL;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci#ifdef CONFIG_BLK_INLINE_ENCRYPTION
16762306a36Sopenharmony_civoid inline_crypt_bio(struct blk_crypto_key *blk_key, struct bio *bio)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	union hp_iv iv;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	memset(&iv, 0, sizeof(union hp_iv));
17262306a36Sopenharmony_ci	iv.index = cpu_to_le64(bio->bi_iter.bi_sector);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	bio_crypt_set_ctx(bio, blk_key, iv.dun, GFP_NOIO);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic struct blk_crypto_key *inline_crypto_init(const u8 *key)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct blk_crypto_key *blk_key = NULL;
18062306a36Sopenharmony_ci	u32 dun_bytes = HP_IV_SIZE - sizeof(__le64);
18162306a36Sopenharmony_ci	int ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	blk_key = kzalloc(sizeof(struct blk_crypto_key), GFP_KERNEL);
18462306a36Sopenharmony_ci	if (!blk_key) {
18562306a36Sopenharmony_ci		pr_err("blk key alloc failed!\n");
18662306a36Sopenharmony_ci		goto err;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	ret = blk_crypto_init_key(blk_key, key, HP_CIPHER_MODE, dun_bytes, PAGE_SIZE);
18962306a36Sopenharmony_ci	if (ret) {
19062306a36Sopenharmony_ci		pr_err("blk key init failed, ret = %d!\n", ret);
19162306a36Sopenharmony_ci		goto err;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return blk_key;
19562306a36Sopenharmony_cierr:
19662306a36Sopenharmony_ci	if (blk_key)
19762306a36Sopenharmony_ci		kfree_sensitive(blk_key);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return NULL;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci#else
20262306a36Sopenharmony_civoid inline_crypt_bio(struct blk_crypto_key *blk_key, struct bio *bio) {}
20362306a36Sopenharmony_cistatic struct blk_crypto_key *inline_crypto_init(const u8 *key)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	pr_err("CONFIG_BLK_INLINE_ENCRYPTION is not enabled!\n");
20662306a36Sopenharmony_ci	return NULL;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci#endif
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cibool crypto_init(struct hp_device *dev, bool soft)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	u8 key[HP_KEY_SIZE];
21362306a36Sopenharmony_ci	bool ret = false;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	get_random_bytes(key, HP_KEY_SIZE);
21662306a36Sopenharmony_ci	if (soft) {
21762306a36Sopenharmony_ci		dev->ctfm = soft_crypto_init(key);
21862306a36Sopenharmony_ci		ret = dev->ctfm;
21962306a36Sopenharmony_ci	} else {
22062306a36Sopenharmony_ci		dev->blk_key = inline_crypto_init(key);
22162306a36Sopenharmony_ci		ret = dev->blk_key;
22262306a36Sopenharmony_ci		if (ret)
22362306a36Sopenharmony_ci			pr_warn("soft crypt has been turned off, now apply hard crypt!\n");
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci	memzero_explicit(key, HP_KEY_SIZE);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_civoid crypto_deinit(struct hp_device *dev)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	if (dev->ctfm) {
23362306a36Sopenharmony_ci		crypto_free_skcipher(dev->ctfm);
23462306a36Sopenharmony_ci		dev->ctfm = NULL;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci	if (dev->blk_key) {
23762306a36Sopenharmony_ci		kfree_sensitive(dev->blk_key);
23862306a36Sopenharmony_ci		dev->blk_key = NULL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
241