scatterwalk.c revision 8fc8598e61f6f384f3eaf1d9b09500c12af47b37
1/* 2 * Cryptographic API. 3 * 4 * Cipher operations. 5 * 6 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> 7 * 2002 Adam J. Richter <adam@yggdrasil.com> 8 * 2004 Jean-Luc Cooke <jlcooke@certainkey.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the Free 12 * Software Foundation; either version 2 of the License, or (at your option) 13 * any later version. 14 * 15 */ 16#include "kmap_types.h" 17 18#include <linux/kernel.h> 19#include <linux/mm.h> 20#include <linux/pagemap.h> 21#include <linux/highmem.h> 22#include <asm/scatterlist.h> 23#include "internal.h" 24#include "scatterwalk.h" 25 26enum km_type crypto_km_types[] = { 27 KM_USER0, 28 KM_USER1, 29 KM_SOFTIRQ0, 30 KM_SOFTIRQ1, 31}; 32 33void *scatterwalk_whichbuf(struct scatter_walk *walk, unsigned int nbytes, void *scratch) 34{ 35 if (nbytes <= walk->len_this_page && 36 (((unsigned long)walk->data) & (PAGE_CACHE_SIZE - 1)) + nbytes <= 37 PAGE_CACHE_SIZE) 38 return walk->data; 39 else 40 return scratch; 41} 42 43static void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out) 44{ 45 if (out) 46 memcpy(sgdata, buf, nbytes); 47 else 48 memcpy(buf, sgdata, nbytes); 49} 50 51void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) 52{ 53 unsigned int rest_of_page; 54 55 walk->sg = sg; 56 57 walk->page = sg->page; 58 walk->len_this_segment = sg->length; 59 60 rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1)); 61 walk->len_this_page = min(sg->length, rest_of_page); 62 walk->offset = sg->offset; 63} 64 65void scatterwalk_map(struct scatter_walk *walk, int out) 66{ 67 walk->data = crypto_kmap(walk->page, out) + walk->offset; 68} 69 70static void scatterwalk_pagedone(struct scatter_walk *walk, int out, 71 unsigned int more) 72{ 73 /* walk->data may be pointing the first byte of the next page; 74 however, we know we transfered at least one byte. So, 75 walk->data - 1 will be a virtual address in the mapped page. */ 76 77 if (out) 78 flush_dcache_page(walk->page); 79 80 if (more) { 81 walk->len_this_segment -= walk->len_this_page; 82 83 if (walk->len_this_segment) { 84 walk->page++; 85 walk->len_this_page = min(walk->len_this_segment, 86 (unsigned)PAGE_CACHE_SIZE); 87 walk->offset = 0; 88 } 89 else 90 scatterwalk_start(walk, sg_next(walk->sg)); 91 } 92} 93 94void scatterwalk_done(struct scatter_walk *walk, int out, int more) 95{ 96 crypto_kunmap(walk->data, out); 97 if (walk->len_this_page == 0 || !more) 98 scatterwalk_pagedone(walk, out, more); 99} 100 101/* 102 * Do not call this unless the total length of all of the fragments 103 * has been verified as multiple of the block size. 104 */ 105int scatterwalk_copychunks(void *buf, struct scatter_walk *walk, 106 size_t nbytes, int out) 107{ 108 if (buf != walk->data) { 109 while (nbytes > walk->len_this_page) { 110 memcpy_dir(buf, walk->data, walk->len_this_page, out); 111 buf += walk->len_this_page; 112 nbytes -= walk->len_this_page; 113 114 crypto_kunmap(walk->data, out); 115 scatterwalk_pagedone(walk, out, 1); 116 scatterwalk_map(walk, out); 117 } 118 119 memcpy_dir(buf, walk->data, nbytes, out); 120 } 121 122 walk->offset += nbytes; 123 walk->len_this_page -= nbytes; 124 walk->len_this_segment -= nbytes; 125 return 0; 126} 127