1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 274ed87e7STero Kristo /* 374ed87e7STero Kristo * OMAP Crypto driver common support routines. 474ed87e7STero Kristo * 574ed87e7STero Kristo * Copyright (c) 2017 Texas Instruments Incorporated 674ed87e7STero Kristo * Tero Kristo <t-kristo@ti.com> 774ed87e7STero Kristo */ 874ed87e7STero Kristo 974ed87e7STero Kristo #include <linux/module.h> 1074ed87e7STero Kristo #include <linux/kernel.h> 1174ed87e7STero Kristo #include <linux/scatterlist.h> 1274ed87e7STero Kristo #include <crypto/scatterwalk.h> 1374ed87e7STero Kristo 1474ed87e7STero Kristo #include "omap-crypto.h" 1574ed87e7STero Kristo 1674ed87e7STero Kristo static int omap_crypto_copy_sg_lists(int total, int bs, 1774ed87e7STero Kristo struct scatterlist **sg, 1874ed87e7STero Kristo struct scatterlist *new_sg, u16 flags) 1974ed87e7STero Kristo { 2074ed87e7STero Kristo int n = sg_nents(*sg); 2174ed87e7STero Kristo struct scatterlist *tmp; 2274ed87e7STero Kristo 2374ed87e7STero Kristo if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) { 2474ed87e7STero Kristo new_sg = kmalloc_array(n, sizeof(*sg), GFP_KERNEL); 2574ed87e7STero Kristo if (!new_sg) 2674ed87e7STero Kristo return -ENOMEM; 2774ed87e7STero Kristo 2874ed87e7STero Kristo sg_init_table(new_sg, n); 2974ed87e7STero Kristo } 3074ed87e7STero Kristo 3174ed87e7STero Kristo tmp = new_sg; 3274ed87e7STero Kristo 3374ed87e7STero Kristo while (*sg && total) { 3474ed87e7STero Kristo int len = (*sg)->length; 3574ed87e7STero Kristo 3674ed87e7STero Kristo if (total < len) 3774ed87e7STero Kristo len = total; 3874ed87e7STero Kristo 3974ed87e7STero Kristo if (len > 0) { 4074ed87e7STero Kristo total -= len; 4174ed87e7STero Kristo sg_set_page(tmp, sg_page(*sg), len, (*sg)->offset); 4274ed87e7STero Kristo if (total <= 0) 4374ed87e7STero Kristo sg_mark_end(tmp); 4474ed87e7STero Kristo tmp = sg_next(tmp); 4574ed87e7STero Kristo } 4674ed87e7STero Kristo 4774ed87e7STero Kristo *sg = sg_next(*sg); 4874ed87e7STero Kristo } 4974ed87e7STero Kristo 5074ed87e7STero Kristo *sg = new_sg; 5174ed87e7STero Kristo 5274ed87e7STero Kristo return 0; 5374ed87e7STero Kristo } 5474ed87e7STero Kristo 5574ed87e7STero Kristo static int omap_crypto_copy_sgs(int total, int bs, struct scatterlist **sg, 5674ed87e7STero Kristo struct scatterlist *new_sg, u16 flags) 5774ed87e7STero Kristo { 5874ed87e7STero Kristo void *buf; 5974ed87e7STero Kristo int pages; 6074ed87e7STero Kristo int new_len; 6174ed87e7STero Kristo 6274ed87e7STero Kristo new_len = ALIGN(total, bs); 6374ed87e7STero Kristo pages = get_order(new_len); 6474ed87e7STero Kristo 6574ed87e7STero Kristo buf = (void *)__get_free_pages(GFP_ATOMIC, pages); 6674ed87e7STero Kristo if (!buf) { 6774ed87e7STero Kristo pr_err("%s: Couldn't allocate pages for unaligned cases.\n", 6874ed87e7STero Kristo __func__); 6974ed87e7STero Kristo return -ENOMEM; 7074ed87e7STero Kristo } 7174ed87e7STero Kristo 7274ed87e7STero Kristo if (flags & OMAP_CRYPTO_COPY_DATA) { 7374ed87e7STero Kristo scatterwalk_map_and_copy(buf, *sg, 0, total, 0); 7474ed87e7STero Kristo if (flags & OMAP_CRYPTO_ZERO_BUF) 7574ed87e7STero Kristo memset(buf + total, 0, new_len - total); 7674ed87e7STero Kristo } 7774ed87e7STero Kristo 7874ed87e7STero Kristo if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 7974ed87e7STero Kristo sg_init_table(new_sg, 1); 8074ed87e7STero Kristo 8174ed87e7STero Kristo sg_set_buf(new_sg, buf, new_len); 8274ed87e7STero Kristo 8374ed87e7STero Kristo *sg = new_sg; 8474ed87e7STero Kristo 8574ed87e7STero Kristo return 0; 8674ed87e7STero Kristo } 8774ed87e7STero Kristo 8874ed87e7STero Kristo static int omap_crypto_check_sg(struct scatterlist *sg, int total, int bs, 8974ed87e7STero Kristo u16 flags) 9074ed87e7STero Kristo { 9174ed87e7STero Kristo int len = 0; 9274ed87e7STero Kristo int num_sg = 0; 9374ed87e7STero Kristo 9474ed87e7STero Kristo if (!IS_ALIGNED(total, bs)) 9574ed87e7STero Kristo return OMAP_CRYPTO_NOT_ALIGNED; 9674ed87e7STero Kristo 9774ed87e7STero Kristo while (sg) { 9874ed87e7STero Kristo num_sg++; 9974ed87e7STero Kristo 10074ed87e7STero Kristo if (!IS_ALIGNED(sg->offset, 4)) 10174ed87e7STero Kristo return OMAP_CRYPTO_NOT_ALIGNED; 10274ed87e7STero Kristo if (!IS_ALIGNED(sg->length, bs)) 10374ed87e7STero Kristo return OMAP_CRYPTO_NOT_ALIGNED; 104065fa252STero Kristo #ifdef CONFIG_ZONE_DMA 105065fa252STero Kristo if (page_zonenum(sg_page(sg)) != ZONE_DMA) 106065fa252STero Kristo return OMAP_CRYPTO_NOT_ALIGNED; 107065fa252STero Kristo #endif 10874ed87e7STero Kristo 10974ed87e7STero Kristo len += sg->length; 11074ed87e7STero Kristo sg = sg_next(sg); 11174ed87e7STero Kristo 11274ed87e7STero Kristo if (len >= total) 11374ed87e7STero Kristo break; 11474ed87e7STero Kristo } 11574ed87e7STero Kristo 11674ed87e7STero Kristo if ((flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) && num_sg > 1) 11774ed87e7STero Kristo return OMAP_CRYPTO_NOT_ALIGNED; 11874ed87e7STero Kristo 11974ed87e7STero Kristo if (len != total) 12074ed87e7STero Kristo return OMAP_CRYPTO_BAD_DATA_LENGTH; 12174ed87e7STero Kristo 12274ed87e7STero Kristo return 0; 12374ed87e7STero Kristo } 12474ed87e7STero Kristo 12574ed87e7STero Kristo int omap_crypto_align_sg(struct scatterlist **sg, int total, int bs, 12674ed87e7STero Kristo struct scatterlist *new_sg, u16 flags, 12774ed87e7STero Kristo u8 flags_shift, unsigned long *dd_flags) 12874ed87e7STero Kristo { 12974ed87e7STero Kristo int ret; 13074ed87e7STero Kristo 13174ed87e7STero Kristo *dd_flags &= ~(OMAP_CRYPTO_COPY_MASK << flags_shift); 13274ed87e7STero Kristo 13374ed87e7STero Kristo if (flags & OMAP_CRYPTO_FORCE_COPY) 13474ed87e7STero Kristo ret = OMAP_CRYPTO_NOT_ALIGNED; 13574ed87e7STero Kristo else 13674ed87e7STero Kristo ret = omap_crypto_check_sg(*sg, total, bs, flags); 13774ed87e7STero Kristo 13874ed87e7STero Kristo if (ret == OMAP_CRYPTO_NOT_ALIGNED) { 13974ed87e7STero Kristo ret = omap_crypto_copy_sgs(total, bs, sg, new_sg, flags); 14074ed87e7STero Kristo if (ret) 14174ed87e7STero Kristo return ret; 14274ed87e7STero Kristo *dd_flags |= OMAP_CRYPTO_DATA_COPIED << flags_shift; 14374ed87e7STero Kristo } else if (ret == OMAP_CRYPTO_BAD_DATA_LENGTH) { 14474ed87e7STero Kristo ret = omap_crypto_copy_sg_lists(total, bs, sg, new_sg, flags); 14574ed87e7STero Kristo if (ret) 14674ed87e7STero Kristo return ret; 14774ed87e7STero Kristo if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 14874ed87e7STero Kristo *dd_flags |= OMAP_CRYPTO_SG_COPIED << flags_shift; 14974ed87e7STero Kristo } else if (flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) { 15074ed87e7STero Kristo sg_set_buf(new_sg, sg_virt(*sg), (*sg)->length); 15174ed87e7STero Kristo } 15274ed87e7STero Kristo 15374ed87e7STero Kristo return 0; 15474ed87e7STero Kristo } 15574ed87e7STero Kristo EXPORT_SYMBOL_GPL(omap_crypto_align_sg); 15674ed87e7STero Kristo 157*839bb2a9STero Kristo static void omap_crypto_copy_data(struct scatterlist *src, 158*839bb2a9STero Kristo struct scatterlist *dst, 159*839bb2a9STero Kristo int offset, int len) 160*839bb2a9STero Kristo { 161*839bb2a9STero Kristo int amt; 162*839bb2a9STero Kristo void *srcb, *dstb; 163*839bb2a9STero Kristo int srco = 0, dsto = offset; 164*839bb2a9STero Kristo 165*839bb2a9STero Kristo while (src && dst && len) { 166*839bb2a9STero Kristo if (srco >= src->length) { 167*839bb2a9STero Kristo srco -= src->length; 168*839bb2a9STero Kristo src = sg_next(src); 169*839bb2a9STero Kristo continue; 170*839bb2a9STero Kristo } 171*839bb2a9STero Kristo 172*839bb2a9STero Kristo if (dsto >= dst->length) { 173*839bb2a9STero Kristo dsto -= dst->length; 174*839bb2a9STero Kristo dst = sg_next(dst); 175*839bb2a9STero Kristo continue; 176*839bb2a9STero Kristo } 177*839bb2a9STero Kristo 178*839bb2a9STero Kristo amt = min(src->length - srco, dst->length - dsto); 179*839bb2a9STero Kristo amt = min(len, amt); 180*839bb2a9STero Kristo 181*839bb2a9STero Kristo srcb = sg_virt(src) + srco; 182*839bb2a9STero Kristo dstb = sg_virt(dst) + dsto; 183*839bb2a9STero Kristo 184*839bb2a9STero Kristo memcpy(dstb, srcb, amt); 185*839bb2a9STero Kristo 186*839bb2a9STero Kristo srco += amt; 187*839bb2a9STero Kristo dsto += amt; 188*839bb2a9STero Kristo len -= amt; 189*839bb2a9STero Kristo } 190*839bb2a9STero Kristo } 191*839bb2a9STero Kristo 19274ed87e7STero Kristo void omap_crypto_cleanup(struct scatterlist *sg, struct scatterlist *orig, 19374ed87e7STero Kristo int offset, int len, u8 flags_shift, 19474ed87e7STero Kristo unsigned long flags) 19574ed87e7STero Kristo { 19674ed87e7STero Kristo void *buf; 19774ed87e7STero Kristo int pages; 19874ed87e7STero Kristo 19974ed87e7STero Kristo flags >>= flags_shift; 20074ed87e7STero Kristo flags &= OMAP_CRYPTO_COPY_MASK; 20174ed87e7STero Kristo 20274ed87e7STero Kristo if (!flags) 20374ed87e7STero Kristo return; 20474ed87e7STero Kristo 20574ed87e7STero Kristo buf = sg_virt(sg); 20674ed87e7STero Kristo pages = get_order(len); 20774ed87e7STero Kristo 20874ed87e7STero Kristo if (orig && (flags & OMAP_CRYPTO_COPY_MASK)) 209*839bb2a9STero Kristo omap_crypto_copy_data(sg, orig, offset, len); 21074ed87e7STero Kristo 21174ed87e7STero Kristo if (flags & OMAP_CRYPTO_DATA_COPIED) 21274ed87e7STero Kristo free_pages((unsigned long)buf, pages); 21374ed87e7STero Kristo else if (flags & OMAP_CRYPTO_SG_COPIED) 21474ed87e7STero Kristo kfree(sg); 21574ed87e7STero Kristo } 21674ed87e7STero Kristo EXPORT_SYMBOL_GPL(omap_crypto_cleanup); 21774ed87e7STero Kristo 21874ed87e7STero Kristo MODULE_DESCRIPTION("OMAP crypto support library."); 21974ed87e7STero Kristo MODULE_LICENSE("GPL v2"); 22074ed87e7STero Kristo MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>"); 221