xref: /openbmc/linux/crypto/scatterwalk.c (revision 1da177e4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Cryptographic API.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Cipher operations.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
71da177e4SLinus Torvalds  *               2002 Adam J. Richter <adam@yggdrasil.com>
81da177e4SLinus Torvalds  *               2004 Jean-Luc Cooke <jlcooke@certainkey.com>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
111da177e4SLinus Torvalds  * under the terms of the GNU General Public License as published by the Free
121da177e4SLinus Torvalds  * Software Foundation; either version 2 of the License, or (at your option)
131da177e4SLinus Torvalds  * any later version.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/mm.h>
181da177e4SLinus Torvalds #include <linux/pagemap.h>
191da177e4SLinus Torvalds #include <linux/highmem.h>
201da177e4SLinus Torvalds #include <asm/bug.h>
211da177e4SLinus Torvalds #include <asm/scatterlist.h>
221da177e4SLinus Torvalds #include "internal.h"
231da177e4SLinus Torvalds #include "scatterwalk.h"
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds enum km_type crypto_km_types[] = {
261da177e4SLinus Torvalds 	KM_USER0,
271da177e4SLinus Torvalds 	KM_USER1,
281da177e4SLinus Torvalds 	KM_SOFTIRQ0,
291da177e4SLinus Torvalds 	KM_SOFTIRQ1,
301da177e4SLinus Torvalds };
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds static void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	if (out)
351da177e4SLinus Torvalds 		memcpy(sgdata, buf, nbytes);
361da177e4SLinus Torvalds 	else
371da177e4SLinus Torvalds 		memcpy(buf, sgdata, nbytes);
381da177e4SLinus Torvalds }
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg)
411da177e4SLinus Torvalds {
421da177e4SLinus Torvalds 	unsigned int rest_of_page;
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 	walk->sg = sg;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	walk->page = sg->page;
471da177e4SLinus Torvalds 	walk->len_this_segment = sg->length;
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 	BUG_ON(!sg->length);
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1));
521da177e4SLinus Torvalds 	walk->len_this_page = min(sg->length, rest_of_page);
531da177e4SLinus Torvalds 	walk->offset = sg->offset;
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds void scatterwalk_map(struct scatter_walk *walk, int out)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	walk->data = crypto_kmap(walk->page, out) + walk->offset;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds static inline void scatterwalk_unmap(struct scatter_walk *walk, int out)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	/* walk->data may be pointing the first byte of the next page;
641da177e4SLinus Torvalds 	   however, we know we transfered at least one byte.  So,
651da177e4SLinus Torvalds 	   walk->data - 1 will be a virtual address in the mapped page. */
661da177e4SLinus Torvalds 	crypto_kunmap(walk->data - 1, out);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds static void scatterwalk_pagedone(struct scatter_walk *walk, int out,
701da177e4SLinus Torvalds 				 unsigned int more)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	if (out)
731da177e4SLinus Torvalds 		flush_dcache_page(walk->page);
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	if (more) {
761da177e4SLinus Torvalds 		walk->len_this_segment -= walk->len_this_page;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 		if (walk->len_this_segment) {
791da177e4SLinus Torvalds 			walk->page++;
801da177e4SLinus Torvalds 			walk->len_this_page = min(walk->len_this_segment,
811da177e4SLinus Torvalds 						  (unsigned)PAGE_CACHE_SIZE);
821da177e4SLinus Torvalds 			walk->offset = 0;
831da177e4SLinus Torvalds 		}
841da177e4SLinus Torvalds 		else
851da177e4SLinus Torvalds 			scatterwalk_start(walk, sg_next(walk->sg));
861da177e4SLinus Torvalds 	}
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds void scatterwalk_done(struct scatter_walk *walk, int out, int more)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	scatterwalk_unmap(walk, out);
921da177e4SLinus Torvalds 	if (walk->len_this_page == 0 || !more)
931da177e4SLinus Torvalds 		scatterwalk_pagedone(walk, out, more);
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds /*
971da177e4SLinus Torvalds  * Do not call this unless the total length of all of the fragments
981da177e4SLinus Torvalds  * has been verified as multiple of the block size.
991da177e4SLinus Torvalds  */
1001da177e4SLinus Torvalds int scatterwalk_copychunks(void *buf, struct scatter_walk *walk,
1011da177e4SLinus Torvalds 			   size_t nbytes, int out)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	do {
1041da177e4SLinus Torvalds 		memcpy_dir(buf, walk->data, walk->len_this_page, out);
1051da177e4SLinus Torvalds 		buf += walk->len_this_page;
1061da177e4SLinus Torvalds 		nbytes -= walk->len_this_page;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 		scatterwalk_unmap(walk, out);
1091da177e4SLinus Torvalds 		scatterwalk_pagedone(walk, out, 1);
1101da177e4SLinus Torvalds 		scatterwalk_map(walk, out);
1111da177e4SLinus Torvalds 	} while (nbytes > walk->len_this_page);
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	memcpy_dir(buf, walk->data, nbytes, out);
1141da177e4SLinus Torvalds 	return nbytes;
1151da177e4SLinus Torvalds }
116