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 108 len += sg->length; 109 sg = sg_next(sg); 110 111 if (len >= total) 112 break; 113 } 114 115 if ((flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) && num_sg > 1) 116 return OMAP_CRYPTO_NOT_ALIGNED; 117 118 if (len != total) 119 return OMAP_CRYPTO_BAD_DATA_LENGTH; 120 121 return 0; 122 } 123 124 int omap_crypto_align_sg(struct scatterlist **sg, int total, int bs, 125 struct scatterlist *new_sg, u16 flags, 126 u8 flags_shift, unsigned long *dd_flags) 127 { 128 int ret; 129 130 *dd_flags &= ~(OMAP_CRYPTO_COPY_MASK << flags_shift); 131 132 if (flags & OMAP_CRYPTO_FORCE_COPY) 133 ret = OMAP_CRYPTO_NOT_ALIGNED; 134 else 135 ret = omap_crypto_check_sg(*sg, total, bs, flags); 136 137 if (ret == OMAP_CRYPTO_NOT_ALIGNED) { 138 ret = omap_crypto_copy_sgs(total, bs, sg, new_sg, flags); 139 if (ret) 140 return ret; 141 *dd_flags |= OMAP_CRYPTO_DATA_COPIED << flags_shift; 142 } else if (ret == OMAP_CRYPTO_BAD_DATA_LENGTH) { 143 ret = omap_crypto_copy_sg_lists(total, bs, sg, new_sg, flags); 144 if (ret) 145 return ret; 146 if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) 147 *dd_flags |= OMAP_CRYPTO_SG_COPIED << flags_shift; 148 } else if (flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) { 149 sg_set_buf(new_sg, sg_virt(*sg), (*sg)->length); 150 } 151 152 return 0; 153 } 154 EXPORT_SYMBOL_GPL(omap_crypto_align_sg); 155 156 void omap_crypto_cleanup(struct scatterlist *sg, struct scatterlist *orig, 157 int offset, int len, u8 flags_shift, 158 unsigned long flags) 159 { 160 void *buf; 161 int pages; 162 163 flags >>= flags_shift; 164 flags &= OMAP_CRYPTO_COPY_MASK; 165 166 if (!flags) 167 return; 168 169 buf = sg_virt(sg); 170 pages = get_order(len); 171 172 if (orig && (flags & OMAP_CRYPTO_COPY_MASK)) 173 scatterwalk_map_and_copy(buf, orig, offset, len, 1); 174 175 if (flags & OMAP_CRYPTO_DATA_COPIED) 176 free_pages((unsigned long)buf, pages); 177 else if (flags & OMAP_CRYPTO_SG_COPIED) 178 kfree(sg); 179 } 180 EXPORT_SYMBOL_GPL(omap_crypto_cleanup); 181 182 MODULE_DESCRIPTION("OMAP crypto support library."); 183 MODULE_LICENSE("GPL v2"); 184 MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>"); 185