2 * Driver for /dev/crypto device (aka CryptoDev)
4 * Copyright (c) 2009-2011 Nikos Mavrogiannopoulos <nmav@gnutls.org>
5 * Copyright (c) 2010 Phil Sutter
7 * This file is part of linux cryptodev.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include <crypto/hash.h>
26 #include <linux/crypto.h>
28 #include <linux/highmem.h>
29 #include <linux/ioctl.h>
30 #include <linux/random.h>
31 #include <linux/syscalls.h>
32 #include <linux/pagemap.h>
33 #include <linux/uaccess.h>
34 #include <crypto/scatterwalk.h>
35 #include <linux/scatterlist.h>
36 #include "cryptodev_int.h"
40 /* Helper functions to assist zero copy.
41 * This needs to be redesigned and moved out of the session. --nmav
44 /* offset of buf in it's first page */
45 #define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK)
47 /* fetch the pages addr resides in into pg and initialise sg with them */
48 int __get_userbuf(uint8_t __user
*addr
, uint32_t len
, int write
,
49 int pgcount
, struct page
**pg
, struct scatterlist
*sg
,
50 struct task_struct
*task
, struct mm_struct
*mm
)
52 int ret
, pglen
, i
= 0;
53 struct scatterlist
*sgp
;
55 down_write(&mm
->mmap_sem
);
56 ret
= get_user_pages(task
, mm
,
57 (unsigned long)addr
, pgcount
, write
, 0, pg
, NULL
);
58 up_write(&mm
->mmap_sem
);
62 sg_init_table(sg
, pgcount
);
64 pglen
= min((ptrdiff_t)(PAGE_SIZE
- PAGEOFFSET(addr
)), (ptrdiff_t)len
);
65 sg_set_page(sg
, pg
[i
++], pglen
, PAGEOFFSET(addr
));
68 for (sgp
= sg_next(sg
); len
; sgp
= sg_next(sgp
)) {
69 pglen
= min((uint32_t)PAGE_SIZE
, len
);
70 sg_set_page(sgp
, pg
[i
++], pglen
, 0);
73 sg_mark_end(sg_last(sg
, pgcount
));
77 int adjust_sg_array(struct csession
* ses
, int pagecount
)
79 struct scatterlist
*sg
;
83 for (array_size
= ses
->array_size
; array_size
< pagecount
;
87 dprintk(2, KERN_DEBUG
, "%s: reallocating to %d elements\n",
88 __func__
, array_size
);
89 pages
= krealloc(ses
->pages
, array_size
* sizeof(struct page
*),
94 sg
= krealloc(ses
->sg
, array_size
* sizeof(struct scatterlist
),
99 ses
->array_size
= array_size
;
104 void release_user_pages(struct page
**pg
, int pagecount
)
106 while (pagecount
--) {
107 if (!PageReserved(pg
[pagecount
]))
108 SetPageDirty(pg
[pagecount
]);
109 page_cache_release(pg
[pagecount
]);
113 /* make src and dst available in scatterlists.
114 * dst might be the same as src.
116 int get_userbuf(struct csession
*ses
, void* __user src
, int src_len
,
117 void* __user dst
, int dst_len
,
118 struct task_struct
*task
, struct mm_struct
*mm
,
119 struct scatterlist
**src_sg
,
120 struct scatterlist
**dst_sg
,
123 int src_pagecount
, dst_pagecount
= 0, pagecount
, write_src
= 1;
129 if (ses
->alignmask
&& !IS_ALIGNED((unsigned long)src
, ses
->alignmask
)) {
130 dprintk(2, KERN_WARNING
, "%s: careful - source address %lx is not %d byte aligned\n",
131 __func__
, (unsigned long)src
, ses
->alignmask
+ 1);
136 src_len
= max(src_len
, dst_len
);
140 src_pagecount
= PAGECOUNT(src
, src_len
);
141 if (!ses
->cdata
.init
) { /* hashing only */
143 } else if (src
!= dst
) { /* non-in-situ transformation */
147 dst_pagecount
= PAGECOUNT(dst
, dst_len
);
150 if (ses
->alignmask
&& !IS_ALIGNED((unsigned long)dst
, ses
->alignmask
)) {
151 dprintk(2, KERN_WARNING
, "%s: careful - destination address %lx is not %d byte aligned\n",
152 __func__
, (unsigned long)dst
, ses
->alignmask
+ 1);
155 (*tot_pages
) = pagecount
= src_pagecount
+ dst_pagecount
;
157 if (pagecount
> ses
->array_size
) {
158 rc
= adjust_sg_array(ses
, pagecount
);
163 rc
= __get_userbuf(src
, src_len
, write_src
, src_pagecount
,
164 ses
->pages
, ses
->sg
, task
, mm
);
167 "failed to get user pages for data input\n");
170 (*src_sg
) = (*dst_sg
) = ses
->sg
;
175 (*dst_sg
) = ses
->sg
+ src_pagecount
;
177 rc
= __get_userbuf(dst
, dst_len
, 1, dst_pagecount
,
178 ses
->pages
+ src_pagecount
, *dst_sg
,
182 "failed to get user pages for data output\n");
183 release_user_pages(ses
->pages
, src_pagecount
);