162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP Crypto driver common support routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017 Texas Instruments Incorporated 662306a36Sopenharmony_ci * Tero Kristo <t-kristo@ti.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/scatterlist.h> 1262306a36Sopenharmony_ci#include <crypto/scatterwalk.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "omap-crypto.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int omap_crypto_copy_sg_lists(int total, int bs, 1762306a36Sopenharmony_ci struct scatterlist **sg, 1862306a36Sopenharmony_ci struct scatterlist *new_sg, u16 flags) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci int n = sg_nents(*sg); 2162306a36Sopenharmony_ci struct scatterlist *tmp; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) { 2462306a36Sopenharmony_ci new_sg = kmalloc_array(n, sizeof(*sg), GFP_KERNEL); 2562306a36Sopenharmony_ci if (!new_sg) 2662306a36Sopenharmony_ci return -ENOMEM; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci sg_init_table(new_sg, n); 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci tmp = new_sg; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci while (*sg && total) { 3462306a36Sopenharmony_ci int len = (*sg)->length; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (total < len) 3762306a36Sopenharmony_ci len = total; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (len > 0) { 4062306a36Sopenharmony_ci total -= len; 4162306a36Sopenharmony_ci sg_set_page(tmp, sg_page(*sg), len, (*sg)->offset); 4262306a36Sopenharmony_ci if (total <= 0) 4362306a36Sopenharmony_ci sg_mark_end(tmp); 4462306a36Sopenharmony_ci tmp = sg_next(tmp); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci *sg = sg_next(*sg); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci *sg = new_sg; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int omap_crypto_copy_sgs(int total, int bs, struct scatterlist **sg, 5662306a36Sopenharmony_ci struct scatterlist *new_sg, u16 flags) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci void *buf; 5962306a36Sopenharmony_ci int pages; 6062306a36Sopenharmony_ci int new_len; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci new_len = ALIGN(total, bs); 6362306a36Sopenharmony_ci pages = get_order(new_len); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci buf = (void *)__get_free_pages(GFP_ATOMIC, pages); 6662306a36Sopenharmony_ci if (!buf) { 6762306a36Sopenharmony_ci pr_err("%s: Couldn't allocate pages for unaligned cases.\n", 6862306a36Sopenharmony_ci __func__); 6962306a36Sopenharmony_ci return -ENOMEM; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (flags & OMAP_CRYPTO_COPY_DATA) { 7362306a36Sopenharmony_ci scatterwalk_map_and_copy(buf, *sg, 0, total, 0); 7462306a36Sopenharmony_ci if (flags & OMAP_CRYPTO_ZERO_BUF) 7562306a36Sopenharmony_ci memset(buf + total, 0, new_len - total); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 7962306a36Sopenharmony_ci sg_init_table(new_sg, 1); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci sg_set_buf(new_sg, buf, new_len); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci *sg = new_sg; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int omap_crypto_check_sg(struct scatterlist *sg, int total, int bs, 8962306a36Sopenharmony_ci u16 flags) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int len = 0; 9262306a36Sopenharmony_ci int num_sg = 0; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!IS_ALIGNED(total, bs)) 9562306a36Sopenharmony_ci return OMAP_CRYPTO_NOT_ALIGNED; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci while (sg) { 9862306a36Sopenharmony_ci num_sg++; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!IS_ALIGNED(sg->offset, 4)) 10162306a36Sopenharmony_ci return OMAP_CRYPTO_NOT_ALIGNED; 10262306a36Sopenharmony_ci if (!IS_ALIGNED(sg->length, bs)) 10362306a36Sopenharmony_ci return OMAP_CRYPTO_NOT_ALIGNED; 10462306a36Sopenharmony_ci#ifdef CONFIG_ZONE_DMA 10562306a36Sopenharmony_ci if (page_zonenum(sg_page(sg)) != ZONE_DMA) 10662306a36Sopenharmony_ci return OMAP_CRYPTO_NOT_ALIGNED; 10762306a36Sopenharmony_ci#endif 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci len += sg->length; 11062306a36Sopenharmony_ci sg = sg_next(sg); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (len >= total) 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if ((flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) && num_sg > 1) 11762306a36Sopenharmony_ci return OMAP_CRYPTO_NOT_ALIGNED; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (len != total) 12062306a36Sopenharmony_ci return OMAP_CRYPTO_BAD_DATA_LENGTH; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint omap_crypto_align_sg(struct scatterlist **sg, int total, int bs, 12662306a36Sopenharmony_ci struct scatterlist *new_sg, u16 flags, 12762306a36Sopenharmony_ci u8 flags_shift, unsigned long *dd_flags) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci *dd_flags &= ~(OMAP_CRYPTO_COPY_MASK << flags_shift); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (flags & OMAP_CRYPTO_FORCE_COPY) 13462306a36Sopenharmony_ci ret = OMAP_CRYPTO_NOT_ALIGNED; 13562306a36Sopenharmony_ci else 13662306a36Sopenharmony_ci ret = omap_crypto_check_sg(*sg, total, bs, flags); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (ret == OMAP_CRYPTO_NOT_ALIGNED) { 13962306a36Sopenharmony_ci ret = omap_crypto_copy_sgs(total, bs, sg, new_sg, flags); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci *dd_flags |= OMAP_CRYPTO_DATA_COPIED << flags_shift; 14362306a36Sopenharmony_ci } else if (ret == OMAP_CRYPTO_BAD_DATA_LENGTH) { 14462306a36Sopenharmony_ci ret = omap_crypto_copy_sg_lists(total, bs, sg, new_sg, flags); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 14862306a36Sopenharmony_ci *dd_flags |= OMAP_CRYPTO_SG_COPIED << flags_shift; 14962306a36Sopenharmony_ci } else if (flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) { 15062306a36Sopenharmony_ci sg_set_buf(new_sg, sg_virt(*sg), (*sg)->length); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_crypto_align_sg); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void omap_crypto_copy_data(struct scatterlist *src, 15862306a36Sopenharmony_ci struct scatterlist *dst, 15962306a36Sopenharmony_ci int offset, int len) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int amt; 16262306a36Sopenharmony_ci void *srcb, *dstb; 16362306a36Sopenharmony_ci int srco = 0, dsto = offset; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci while (src && dst && len) { 16662306a36Sopenharmony_ci if (srco >= src->length) { 16762306a36Sopenharmony_ci srco -= src->length; 16862306a36Sopenharmony_ci src = sg_next(src); 16962306a36Sopenharmony_ci continue; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (dsto >= dst->length) { 17362306a36Sopenharmony_ci dsto -= dst->length; 17462306a36Sopenharmony_ci dst = sg_next(dst); 17562306a36Sopenharmony_ci continue; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci amt = min(src->length - srco, dst->length - dsto); 17962306a36Sopenharmony_ci amt = min(len, amt); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci srcb = kmap_atomic(sg_page(src)) + srco + src->offset; 18262306a36Sopenharmony_ci dstb = kmap_atomic(sg_page(dst)) + dsto + dst->offset; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci memcpy(dstb, srcb, amt); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci flush_dcache_page(sg_page(dst)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci kunmap_atomic(srcb); 18962306a36Sopenharmony_ci kunmap_atomic(dstb); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci srco += amt; 19262306a36Sopenharmony_ci dsto += amt; 19362306a36Sopenharmony_ci len -= amt; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_civoid omap_crypto_cleanup(struct scatterlist *sg, struct scatterlist *orig, 19862306a36Sopenharmony_ci int offset, int len, u8 flags_shift, 19962306a36Sopenharmony_ci unsigned long flags) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci void *buf; 20262306a36Sopenharmony_ci int pages; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci flags >>= flags_shift; 20562306a36Sopenharmony_ci flags &= OMAP_CRYPTO_COPY_MASK; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!flags) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci buf = sg_virt(sg); 21162306a36Sopenharmony_ci pages = get_order(len); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (orig && (flags & OMAP_CRYPTO_DATA_COPIED)) 21462306a36Sopenharmony_ci omap_crypto_copy_data(sg, orig, offset, len); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (flags & OMAP_CRYPTO_DATA_COPIED) 21762306a36Sopenharmony_ci free_pages((unsigned long)buf, pages); 21862306a36Sopenharmony_ci else if (flags & OMAP_CRYPTO_SG_COPIED) 21962306a36Sopenharmony_ci kfree(sg); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_crypto_cleanup); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP crypto support library."); 22462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 22562306a36Sopenharmony_ciMODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>"); 226