1 /* 2 * OMAP Crypto driver common support routines. 3 * 4 * Copyright (c) 2017 Texas Instruments Incorporated 5 * Tero Kristo <t-kristo@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as published 9 * by the Free Software Foundation. 10 */ 11 12 #include <linux/module.h> 13 #include <linux/kernel.h> 14 #include <linux/scatterlist.h> 15 #include <crypto/scatterwalk.h> 16 17 #include "omap-crypto.h" 18 19 static int omap_crypto_copy_sg_lists(int total, int bs, 20 struct scatterlist **sg, 21 struct scatterlist *new_sg, u16 flags) 22 { 23 int n = sg_nents(*sg); 24 struct scatterlist *tmp; 25 26 if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) { 27 new_sg = kmalloc_array(n, sizeof(*sg), GFP_KERNEL); 28 if (!new_sg) 29 return -ENOMEM; 30 31 sg_init_table(new_sg, n); 32 } 33 34 tmp = new_sg; 35 36 while (*sg && total) { 37 int len = (*sg)->length; 38 39 if (total < len) 40 len = total; 41 42 if (len > 0) { 43 total -= len; 44 sg_set_page(tmp, sg_page(*sg), len, (*sg)->offset); 45 if (total <= 0) 46 sg_mark_end(tmp); 47 tmp = sg_next(tmp); 48 } 49 50 *sg = sg_next(*sg); 51 } 52 53 *sg = new_sg; 54 55 return 0; 56 } 57 58 static int omap_crypto_copy_sgs(int total, int bs, struct scatterlist **sg, 59 struct scatterlist *new_sg, u16 flags) 60 { 61 void *buf; 62 int pages; 63 int new_len; 64 65 new_len = ALIGN(total, bs); 66 pages = get_order(new_len); 67 68 buf = (void *)__get_free_pages(GFP_ATOMIC, pages); 69 if (!buf) { 70 pr_err("%s: Couldn't allocate pages for unaligned cases.\n", 71 __func__); 72 return -ENOMEM; 73 } 74 75 if (flags & OMAP_CRYPTO_COPY_DATA) { 76 scatterwalk_map_and_copy(buf, *sg, 0, total, 0); 77 if (flags & OMAP_CRYPTO_ZERO_BUF) 78 memset(buf + total, 0, new_len - total); 79 } 80 81 if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 82 sg_init_table(new_sg, 1); 83 84 sg_set_buf(new_sg, buf, new_len); 85 86 *sg = new_sg; 87 88 return 0; 89 } 90 91 static int omap_crypto_check_sg(struct scatterlist *sg, int total, int bs, 92 u16 flags) 93 { 94 int len = 0; 95 int num_sg = 0; 96 97 if (!IS_ALIGNED(total, bs)) 98 return OMAP_CRYPTO_NOT_ALIGNED; 99 100 while (sg) { 101 num_sg++; 102 103 if (!IS_ALIGNED(sg->offset, 4)) 104 return OMAP_CRYPTO_NOT_ALIGNED; 105 if (!IS_ALIGNED(sg->length, bs)) 106 return OMAP_CRYPTO_NOT_ALIGNED; 107 #ifdef CONFIG_ZONE_DMA 108 if (page_zonenum(sg_page(sg)) != ZONE_DMA) 109 return OMAP_CRYPTO_NOT_ALIGNED; 110 #endif 111 112 len += sg->length; 113 sg = sg_next(sg); 114 115 if (len >= total) 116 break; 117 } 118 119 if ((flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) && num_sg > 1) 120 return OMAP_CRYPTO_NOT_ALIGNED; 121 122 if (len != total) 123 return OMAP_CRYPTO_BAD_DATA_LENGTH; 124 125 return 0; 126 } 127 128 int omap_crypto_align_sg(struct scatterlist **sg, int total, int bs, 129 struct scatterlist *new_sg, u16 flags, 130 u8 flags_shift, unsigned long *dd_flags) 131 { 132 int ret; 133 134 *dd_flags &= ~(OMAP_CRYPTO_COPY_MASK << flags_shift); 135 136 if (flags & OMAP_CRYPTO_FORCE_COPY) 137 ret = OMAP_CRYPTO_NOT_ALIGNED; 138 else 139 ret = omap_crypto_check_sg(*sg, total, bs, flags); 140 141 if (ret == OMAP_CRYPTO_NOT_ALIGNED) { 142 ret = omap_crypto_copy_sgs(total, bs, sg, new_sg, flags); 143 if (ret) 144 return ret; 145 *dd_flags |= OMAP_CRYPTO_DATA_COPIED << flags_shift; 146 } else if (ret == OMAP_CRYPTO_BAD_DATA_LENGTH) { 147 ret = omap_crypto_copy_sg_lists(total, bs, sg, new_sg, flags); 148 if (ret) 149 return ret; 150 if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 151 *dd_flags |= OMAP_CRYPTO_SG_COPIED << flags_shift; 152 } else if (flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) { 153 sg_set_buf(new_sg, sg_virt(*sg), (*sg)->length); 154 } 155 156 return 0; 157 } 158 EXPORT_SYMBOL_GPL(omap_crypto_align_sg); 159 160 void omap_crypto_cleanup(struct scatterlist *sg, struct scatterlist *orig, 161 int offset, int len, u8 flags_shift, 162 unsigned long flags) 163 { 164 void *buf; 165 int pages; 166 167 flags >>= flags_shift; 168 flags &= OMAP_CRYPTO_COPY_MASK; 169 170 if (!flags) 171 return; 172 173 buf = sg_virt(sg); 174 pages = get_order(len); 175 176 if (orig && (flags & OMAP_CRYPTO_COPY_MASK)) 177 scatterwalk_map_and_copy(buf, orig, offset, len, 1); 178 179 if (flags & OMAP_CRYPTO_DATA_COPIED) 180 free_pages((unsigned long)buf, pages); 181 else if (flags & OMAP_CRYPTO_SG_COPIED) 182 kfree(sg); 183 } 184 EXPORT_SYMBOL_GPL(omap_crypto_cleanup); 185 186 MODULE_DESCRIPTION("OMAP crypto support library."); 187 MODULE_LICENSE("GPL v2"); 188 MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>"); 189