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 */ 1642c271c6SHerbert Xu 1742c271c6SHerbert Xu #include <crypto/scatterwalk.h> 181da177e4SLinus Torvalds #include <linux/kernel.h> 191da177e4SLinus Torvalds #include <linux/mm.h> 205c64097aSHerbert Xu #include <linux/module.h> 211da177e4SLinus Torvalds #include <linux/pagemap.h> 221da177e4SLinus Torvalds #include <linux/highmem.h> 235c64097aSHerbert Xu #include <linux/scatterlist.h> 245c64097aSHerbert Xu 255c64097aSHerbert Xu static inline void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out) 261da177e4SLinus Torvalds { 275c64097aSHerbert Xu void *src = out ? buf : sgdata; 285c64097aSHerbert Xu void *dst = out ? sgdata : buf; 295c64097aSHerbert Xu 305c64097aSHerbert Xu memcpy(dst, src, nbytes); 311da177e4SLinus Torvalds } 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) 341da177e4SLinus Torvalds { 351da177e4SLinus Torvalds walk->sg = sg; 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds BUG_ON(!sg->length); 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds walk->offset = sg->offset; 401da177e4SLinus Torvalds } 415c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_start); 421da177e4SLinus Torvalds 435c64097aSHerbert Xu void *scatterwalk_map(struct scatter_walk *walk, int out) 441da177e4SLinus Torvalds { 455c64097aSHerbert Xu return crypto_kmap(scatterwalk_page(walk), out) + 465c64097aSHerbert Xu offset_in_page(walk->offset); 471da177e4SLinus Torvalds } 485c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_map); 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds static void scatterwalk_pagedone(struct scatter_walk *walk, int out, 511da177e4SLinus Torvalds unsigned int more) 521da177e4SLinus Torvalds { 539f116727SHerbert Xu if (out) { 549f116727SHerbert Xu struct page *page; 559f116727SHerbert Xu 5678c2f0b8SJens Axboe page = sg_page(walk->sg) + ((walk->offset - 1) >> PAGE_SHIFT); 579f116727SHerbert Xu flush_dcache_page(page); 589f116727SHerbert Xu } 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds if (more) { 615c64097aSHerbert Xu walk->offset += PAGE_SIZE - 1; 625c64097aSHerbert Xu walk->offset &= PAGE_MASK; 635c64097aSHerbert Xu if (walk->offset >= walk->sg->offset + walk->sg->length) 64b2ab4a57SHerbert Xu scatterwalk_start(walk, scatterwalk_sg_next(walk->sg)); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds void scatterwalk_done(struct scatter_walk *walk, int out, int more) 691da177e4SLinus Torvalds { 705c64097aSHerbert Xu if (!offset_in_page(walk->offset) || !more) 711da177e4SLinus Torvalds scatterwalk_pagedone(walk, out, more); 721da177e4SLinus Torvalds } 735c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_done); 741da177e4SLinus Torvalds 755c64097aSHerbert Xu void scatterwalk_copychunks(void *buf, struct scatter_walk *walk, 761da177e4SLinus Torvalds size_t nbytes, int out) 771da177e4SLinus Torvalds { 785c64097aSHerbert Xu for (;;) { 795c64097aSHerbert Xu unsigned int len_this_page = scatterwalk_pagelen(walk); 805c64097aSHerbert Xu u8 *vaddr; 811da177e4SLinus Torvalds 825c64097aSHerbert Xu if (len_this_page > nbytes) 835c64097aSHerbert Xu len_this_page = nbytes; 845c64097aSHerbert Xu 855c64097aSHerbert Xu vaddr = scatterwalk_map(walk, out); 865c64097aSHerbert Xu memcpy_dir(buf, vaddr, len_this_page, out); 875c64097aSHerbert Xu scatterwalk_unmap(vaddr, out); 885c64097aSHerbert Xu 894ee531a3SHerbert Xu scatterwalk_advance(walk, len_this_page); 90f70ee5ecSJ. Bruce Fields 915c64097aSHerbert Xu if (nbytes == len_this_page) 925c64097aSHerbert Xu break; 935c64097aSHerbert Xu 945c64097aSHerbert Xu buf += len_this_page; 955c64097aSHerbert Xu nbytes -= len_this_page; 965c64097aSHerbert Xu 971da177e4SLinus Torvalds scatterwalk_pagedone(walk, out, 1); 98c774e93eSHerbert Xu } 991da177e4SLinus Torvalds } 1005c64097aSHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_copychunks); 1015fa0fea2SHerbert Xu 1025fa0fea2SHerbert Xu void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg, 1035fa0fea2SHerbert Xu unsigned int start, unsigned int nbytes, int out) 1045fa0fea2SHerbert Xu { 1055fa0fea2SHerbert Xu struct scatter_walk walk; 1065fa0fea2SHerbert Xu unsigned int offset = 0; 1075fa0fea2SHerbert Xu 1086e050778SHerbert Xu if (!nbytes) 1096e050778SHerbert Xu return; 1106e050778SHerbert Xu 1115fa0fea2SHerbert Xu for (;;) { 1125fa0fea2SHerbert Xu scatterwalk_start(&walk, sg); 1135fa0fea2SHerbert Xu 1145fa0fea2SHerbert Xu if (start < offset + sg->length) 1155fa0fea2SHerbert Xu break; 1165fa0fea2SHerbert Xu 1175fa0fea2SHerbert Xu offset += sg->length; 118b2ab4a57SHerbert Xu sg = scatterwalk_sg_next(sg); 1195fa0fea2SHerbert Xu } 1205fa0fea2SHerbert Xu 1215fa0fea2SHerbert Xu scatterwalk_advance(&walk, start - offset); 1225fa0fea2SHerbert Xu scatterwalk_copychunks(buf, &walk, nbytes, out); 1235fa0fea2SHerbert Xu scatterwalk_done(&walk, out, 0); 1245fa0fea2SHerbert Xu } 1255fa0fea2SHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy); 126