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