xref: /OK3568_Linux_fs/kernel/drivers/crypto/rockchip/cryptodev_linux/zc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Driver for /dev/crypto device (aka CryptoDev)
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (c) 2009-2013 Nikos Mavrogiannopoulos <nmav@gnutls.org>
5*4882a593Smuzhiyun  * Copyright (c) 2010 Phil Sutter
6*4882a593Smuzhiyun  * Copyright (c) 2011, 2012 OpenSSL Software Foundation, Inc.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This file is part of linux cryptodev.
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or
11*4882a593Smuzhiyun  * modify it under the terms of the GNU General Public License
12*4882a593Smuzhiyun  * as published by the Free Software Foundation; either version 2
13*4882a593Smuzhiyun  * of the License, or (at your option) any later version.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * This program is distributed in the hope that it will be useful,
16*4882a593Smuzhiyun  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*4882a593Smuzhiyun  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*4882a593Smuzhiyun  * GNU General Public License for more details.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  * You should have received a copy of the GNU General Public License
21*4882a593Smuzhiyun  * along with this program; if not, write to the Free Software
22*4882a593Smuzhiyun  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23*4882a593Smuzhiyun  * 02110-1301, USA.
24*4882a593Smuzhiyun  */
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include <crypto/hash.h>
27*4882a593Smuzhiyun #include <linux/crypto.h>
28*4882a593Smuzhiyun #include <linux/mm.h>
29*4882a593Smuzhiyun #include <linux/highmem.h>
30*4882a593Smuzhiyun #include <linux/ioctl.h>
31*4882a593Smuzhiyun #include <linux/random.h>
32*4882a593Smuzhiyun #include <linux/syscalls.h>
33*4882a593Smuzhiyun #include <linux/pagemap.h>
34*4882a593Smuzhiyun #include <linux/uaccess.h>
35*4882a593Smuzhiyun #include <crypto/scatterwalk.h>
36*4882a593Smuzhiyun #include <linux/scatterlist.h>
37*4882a593Smuzhiyun #include "cryptodev.h"
38*4882a593Smuzhiyun #include "zc.h"
39*4882a593Smuzhiyun #include "version.h"
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun /* Helper functions to assist zero copy.
42*4882a593Smuzhiyun  * This needs to be redesigned and moved out of the session. --nmav
43*4882a593Smuzhiyun  */
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* offset of buf in it's first page */
46*4882a593Smuzhiyun #define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK)
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /* fetch the pages addr resides in into pg and initialise sg with them */
__cryptodev_get_userbuf(uint8_t __user * addr,uint32_t len,int write,unsigned int pgcount,struct page ** pg,struct scatterlist * sg,struct task_struct * task,struct mm_struct * mm)49*4882a593Smuzhiyun int __cryptodev_get_userbuf(uint8_t __user *addr, uint32_t len, int write,
50*4882a593Smuzhiyun 		unsigned int pgcount, struct page **pg, struct scatterlist *sg,
51*4882a593Smuzhiyun 		struct task_struct *task, struct mm_struct *mm)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun 	int ret, pglen, i = 0;
54*4882a593Smuzhiyun 	struct scatterlist *sgp;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	if (unlikely(!pgcount || !len || !addr)) {
57*4882a593Smuzhiyun 		sg_mark_end(sg);
58*4882a593Smuzhiyun 		return 0;
59*4882a593Smuzhiyun 	}
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
62*4882a593Smuzhiyun 	down_read(&mm->mmap_sem);
63*4882a593Smuzhiyun #else
64*4882a593Smuzhiyun 	mmap_read_lock(mm);
65*4882a593Smuzhiyun #endif
66*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 168))
67*4882a593Smuzhiyun 	ret = get_user_pages(task, mm,
68*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write, 0, pg, NULL);
69*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
70*4882a593Smuzhiyun 	ret = get_user_pages(task, mm,
71*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write, pg, NULL);
72*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
73*4882a593Smuzhiyun 	ret = get_user_pages_remote(task, mm,
74*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write, 0, pg, NULL);
75*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
76*4882a593Smuzhiyun 	ret = get_user_pages_remote(task, mm,
77*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write ? FOLL_WRITE : 0,
78*4882a593Smuzhiyun 			pg, NULL);
79*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0))
80*4882a593Smuzhiyun 	ret = get_user_pages_remote(task, mm,
81*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write ? FOLL_WRITE : 0,
82*4882a593Smuzhiyun 			pg, NULL, NULL);
83*4882a593Smuzhiyun #else
84*4882a593Smuzhiyun 	ret = get_user_pages_remote(mm,
85*4882a593Smuzhiyun 			(unsigned long)addr, pgcount, write ? FOLL_WRITE : 0,
86*4882a593Smuzhiyun 			pg, NULL, NULL);
87*4882a593Smuzhiyun #endif
88*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
89*4882a593Smuzhiyun 	up_read(&mm->mmap_sem);
90*4882a593Smuzhiyun #else
91*4882a593Smuzhiyun 	mmap_read_unlock(mm);
92*4882a593Smuzhiyun #endif
93*4882a593Smuzhiyun 	if (ret != pgcount)
94*4882a593Smuzhiyun 		return -EINVAL;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	sg_init_table(sg, pgcount);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	pglen = min((ptrdiff_t)(PAGE_SIZE - PAGEOFFSET(addr)), (ptrdiff_t)len);
99*4882a593Smuzhiyun 	sg_set_page(sg, pg[i++], pglen, PAGEOFFSET(addr));
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	len -= pglen;
102*4882a593Smuzhiyun 	for (sgp = sg_next(sg); len; sgp = sg_next(sgp)) {
103*4882a593Smuzhiyun 		pglen = min((uint32_t)PAGE_SIZE, len);
104*4882a593Smuzhiyun 		sg_set_page(sgp, pg[i++], pglen, 0);
105*4882a593Smuzhiyun 		len -= pglen;
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 	sg_mark_end(sg_last(sg, pgcount));
108*4882a593Smuzhiyun 	return 0;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
cryptodev_adjust_sg_array(struct csession * ses,int pagecount)111*4882a593Smuzhiyun int cryptodev_adjust_sg_array(struct csession *ses, int pagecount)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	struct scatterlist *sg;
114*4882a593Smuzhiyun 	struct page **pages;
115*4882a593Smuzhiyun 	int array_size;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	for (array_size = ses->array_size; array_size < pagecount;
118*4882a593Smuzhiyun 	     array_size *= 2)
119*4882a593Smuzhiyun 		;
120*4882a593Smuzhiyun 	ddebug(1, "reallocating from %d to %d pages",
121*4882a593Smuzhiyun 			ses->array_size, array_size);
122*4882a593Smuzhiyun 	pages = krealloc(ses->pages, array_size * sizeof(struct page *),
123*4882a593Smuzhiyun 			 GFP_KERNEL);
124*4882a593Smuzhiyun 	if (unlikely(!pages))
125*4882a593Smuzhiyun 		return -ENOMEM;
126*4882a593Smuzhiyun 	ses->pages = pages;
127*4882a593Smuzhiyun 	sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist),
128*4882a593Smuzhiyun 		      GFP_KERNEL);
129*4882a593Smuzhiyun 	if (unlikely(!sg))
130*4882a593Smuzhiyun 		return -ENOMEM;
131*4882a593Smuzhiyun 	ses->sg = sg;
132*4882a593Smuzhiyun 	ses->array_size = array_size;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
cryptodev_release_user_pages(struct csession * ses)137*4882a593Smuzhiyun void cryptodev_release_user_pages(struct csession *ses)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	unsigned int i;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	for (i = 0; i < ses->used_pages; i++) {
142*4882a593Smuzhiyun 		if (!PageReserved(ses->pages[i]))
143*4882a593Smuzhiyun 			SetPageDirty(ses->pages[i]);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 		if (ses->readonly_pages == 0)
146*4882a593Smuzhiyun 			flush_dcache_page(ses->pages[i]);
147*4882a593Smuzhiyun 		else
148*4882a593Smuzhiyun 			ses->readonly_pages--;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 		put_page(ses->pages[i]);
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 	ses->used_pages = 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun /* make src and dst available in scatterlists.
156*4882a593Smuzhiyun  * dst might be the same as src.
157*4882a593Smuzhiyun  */
cryptodev_get_userbuf(struct csession * ses,void * __user src,unsigned int src_len,void * __user dst,unsigned int dst_len,struct task_struct * task,struct mm_struct * mm,struct scatterlist ** src_sg,struct scatterlist ** dst_sg)158*4882a593Smuzhiyun int cryptodev_get_userbuf(struct csession *ses,
159*4882a593Smuzhiyun                 void *__user src, unsigned int src_len,
160*4882a593Smuzhiyun                 void *__user dst, unsigned int dst_len,
161*4882a593Smuzhiyun                 struct task_struct *task, struct mm_struct *mm,
162*4882a593Smuzhiyun                 struct scatterlist **src_sg,
163*4882a593Smuzhiyun                 struct scatterlist **dst_sg)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun 	int src_pagecount, dst_pagecount;
166*4882a593Smuzhiyun 	int rc;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	/* Empty input is a valid option to many algorithms & is tested by NIST/FIPS */
169*4882a593Smuzhiyun 	/* Make sure NULL input has 0 length */
170*4882a593Smuzhiyun 	if (!src && src_len)
171*4882a593Smuzhiyun 		src_len = 0;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	/* I don't know that null output is ever useful, but we can handle it gracefully */
174*4882a593Smuzhiyun 	/* Make sure NULL output has 0 length */
175*4882a593Smuzhiyun 	if (!dst && dst_len)
176*4882a593Smuzhiyun 		dst_len = 0;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	src_pagecount = PAGECOUNT(src, src_len);
179*4882a593Smuzhiyun 	dst_pagecount = PAGECOUNT(dst, dst_len);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	ses->used_pages = (src == dst) ? max(src_pagecount, dst_pagecount)
182*4882a593Smuzhiyun 	                               : src_pagecount + dst_pagecount;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	ses->readonly_pages = (src == dst) ? 0 : src_pagecount;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (ses->used_pages > ses->array_size) {
187*4882a593Smuzhiyun 		rc = cryptodev_adjust_sg_array(ses, ses->used_pages);
188*4882a593Smuzhiyun 		if (rc)
189*4882a593Smuzhiyun 			return rc;
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	if (src == dst) {	/* inplace operation */
193*4882a593Smuzhiyun 		/* When we encrypt for authenc modes we need to write
194*4882a593Smuzhiyun 		 * more data than the ones we read. */
195*4882a593Smuzhiyun 		if (src_len < dst_len)
196*4882a593Smuzhiyun 			src_len = dst_len;
197*4882a593Smuzhiyun 		rc = __cryptodev_get_userbuf(src, src_len, 1, ses->used_pages,
198*4882a593Smuzhiyun 			               ses->pages, ses->sg, task, mm);
199*4882a593Smuzhiyun 		if (unlikely(rc)) {
200*4882a593Smuzhiyun 			derr(1, "failed to get user pages for data IO");
201*4882a593Smuzhiyun 			return rc;
202*4882a593Smuzhiyun 		}
203*4882a593Smuzhiyun 		(*src_sg) = (*dst_sg) = ses->sg;
204*4882a593Smuzhiyun 		return 0;
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	*src_sg = NULL; /* default to no input */
208*4882a593Smuzhiyun 	*dst_sg = NULL; /* default to ignore output */
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (likely(src)) {
211*4882a593Smuzhiyun 		rc = __cryptodev_get_userbuf(src, src_len, 0, ses->readonly_pages,
212*4882a593Smuzhiyun 					   ses->pages, ses->sg, task, mm);
213*4882a593Smuzhiyun 		if (unlikely(rc)) {
214*4882a593Smuzhiyun 			derr(1, "failed to get user pages for data input");
215*4882a593Smuzhiyun 			return rc;
216*4882a593Smuzhiyun 		}
217*4882a593Smuzhiyun 		*src_sg = ses->sg;
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (likely(dst)) {
221*4882a593Smuzhiyun 		const unsigned int writable_pages =
222*4882a593Smuzhiyun 			ses->used_pages - ses->readonly_pages;
223*4882a593Smuzhiyun 		struct page **dst_pages = ses->pages + ses->readonly_pages;
224*4882a593Smuzhiyun 		*dst_sg = ses->sg + ses->readonly_pages;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 		rc = __cryptodev_get_userbuf(dst, dst_len, 1, writable_pages,
227*4882a593Smuzhiyun 					   dst_pages, *dst_sg, task, mm);
228*4882a593Smuzhiyun 		if (unlikely(rc)) {
229*4882a593Smuzhiyun 			derr(1, "failed to get user pages for data output");
230*4882a593Smuzhiyun 			cryptodev_release_user_pages(ses);  /* FIXME: use __release_userbuf(src, ...) */
231*4882a593Smuzhiyun 			return rc;
232*4882a593Smuzhiyun 		}
233*4882a593Smuzhiyun 	}
234*4882a593Smuzhiyun 	return 0;
235*4882a593Smuzhiyun }
236