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> 185c64097aSHerbert Xu #include <linux/module.h> 191da177e4SLinus Torvalds #include <linux/pagemap.h> 201da177e4SLinus Torvalds #include <linux/highmem.h> 215c64097aSHerbert Xu #include <linux/scatterlist.h> 225c64097aSHerbert Xu 231da177e4SLinus Torvalds #include "internal.h" 241da177e4SLinus Torvalds #include "scatterwalk.h" 251da177e4SLinus Torvalds 265c64097aSHerbert Xu static inline void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out) 271da177e4SLinus Torvalds { 285c64097aSHerbert Xu void *src = out ? buf : sgdata; 295c64097aSHerbert Xu void *dst = out ? sgdata : buf; 305c64097aSHerbert Xu 315c64097aSHerbert Xu memcpy(dst, src, nbytes); 321da177e4SLinus Torvalds } 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds walk->sg = sg; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds BUG_ON(!sg->length); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds walk->offset = sg->offset; 411da177e4SLinus Torvalds } 425c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_start); 431da177e4SLinus Torvalds 445c64097aSHerbert Xu void *scatterwalk_map(struct scatter_walk *walk, int out) 451da177e4SLinus Torvalds { 465c64097aSHerbert Xu return crypto_kmap(scatterwalk_page(walk), out) + 475c64097aSHerbert Xu offset_in_page(walk->offset); 481da177e4SLinus Torvalds } 495c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_map); 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds static void scatterwalk_pagedone(struct scatter_walk *walk, int out, 521da177e4SLinus Torvalds unsigned int more) 531da177e4SLinus Torvalds { 549f116727SHerbert Xu if (out) { 559f116727SHerbert Xu struct page *page; 569f116727SHerbert Xu 5778c2f0b8SJens Axboe page = sg_page(walk->sg) + ((walk->offset - 1) >> PAGE_SHIFT); 589f116727SHerbert Xu flush_dcache_page(page); 599f116727SHerbert Xu } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds if (more) { 625c64097aSHerbert Xu walk->offset += PAGE_SIZE - 1; 635c64097aSHerbert Xu walk->offset &= PAGE_MASK; 645c64097aSHerbert Xu if (walk->offset >= walk->sg->offset + walk->sg->length) 65ab83407eSJens Axboe scatterwalk_start(walk, scatterwalk_sg_next(walk->sg)); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds void scatterwalk_done(struct scatter_walk *walk, int out, int more) 701da177e4SLinus Torvalds { 715c64097aSHerbert Xu if (!offset_in_page(walk->offset) || !more) 721da177e4SLinus Torvalds scatterwalk_pagedone(walk, out, more); 731da177e4SLinus Torvalds } 745c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_done); 751da177e4SLinus Torvalds 765c64097aSHerbert Xu void scatterwalk_copychunks(void *buf, struct scatter_walk *walk, 771da177e4SLinus Torvalds size_t nbytes, int out) 781da177e4SLinus Torvalds { 795c64097aSHerbert Xu for (;;) { 805c64097aSHerbert Xu unsigned int len_this_page = scatterwalk_pagelen(walk); 815c64097aSHerbert Xu u8 *vaddr; 821da177e4SLinus Torvalds 835c64097aSHerbert Xu if (len_this_page > nbytes) 845c64097aSHerbert Xu len_this_page = nbytes; 855c64097aSHerbert Xu 865c64097aSHerbert Xu vaddr = scatterwalk_map(walk, out); 875c64097aSHerbert Xu memcpy_dir(buf, vaddr, len_this_page, out); 885c64097aSHerbert Xu scatterwalk_unmap(vaddr, out); 895c64097aSHerbert Xu 904ee531a3SHerbert Xu scatterwalk_advance(walk, len_this_page); 91f70ee5ecSJ. Bruce Fields 925c64097aSHerbert Xu if (nbytes == len_this_page) 935c64097aSHerbert Xu break; 945c64097aSHerbert Xu 955c64097aSHerbert Xu buf += len_this_page; 965c64097aSHerbert Xu nbytes -= len_this_page; 975c64097aSHerbert Xu 981da177e4SLinus Torvalds scatterwalk_pagedone(walk, out, 1); 99c774e93eSHerbert Xu } 1001da177e4SLinus Torvalds } 1015c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_copychunks); 1025fa0fea2SHerbert Xu 1035fa0fea2SHerbert Xu void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg, 1045fa0fea2SHerbert Xu unsigned int start, unsigned int nbytes, int out) 1055fa0fea2SHerbert Xu { 1065fa0fea2SHerbert Xu struct scatter_walk walk; 1075fa0fea2SHerbert Xu unsigned int offset = 0; 1085fa0fea2SHerbert Xu 1095fa0fea2SHerbert Xu for (;;) { 1105fa0fea2SHerbert Xu scatterwalk_start(&walk, sg); 1115fa0fea2SHerbert Xu 1125fa0fea2SHerbert Xu if (start < offset + sg->length) 1135fa0fea2SHerbert Xu break; 1145fa0fea2SHerbert Xu 1155fa0fea2SHerbert Xu offset += sg->length; 1165fa0fea2SHerbert Xu sg = sg_next(sg); 1175fa0fea2SHerbert Xu } 1185fa0fea2SHerbert Xu 1195fa0fea2SHerbert Xu scatterwalk_advance(&walk, start - offset); 1205fa0fea2SHerbert Xu scatterwalk_copychunks(buf, &walk, nbytes, out); 1215fa0fea2SHerbert Xu scatterwalk_done(&walk, out, 0); 1225fa0fea2SHerbert Xu } 1235fa0fea2SHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy); 124