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 43f0dfc0b0SCong Wang void *scatterwalk_map(struct scatter_walk *walk) 441da177e4SLinus Torvalds { 45f0dfc0b0SCong Wang return kmap_atomic(scatterwalk_page(walk)) + 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); 574f3e797aSHerbert Xu if (!PageSlab(page)) 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) 65b2ab4a57SHerbert Xu 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 { 7185c6201aSDavid S. Miller if (!(scatterwalk_pagelen(walk) & (PAGE_SIZE - 1)) || !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 86f0dfc0b0SCong Wang vaddr = scatterwalk_map(walk); 875c64097aSHerbert Xu memcpy_dir(buf, vaddr, len_this_page, out); 88f0dfc0b0SCong Wang scatterwalk_unmap(vaddr); 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 1096e050778SHerbert Xu if (!nbytes) 1106e050778SHerbert Xu return; 1116e050778SHerbert Xu 1125fa0fea2SHerbert Xu for (;;) { 1135fa0fea2SHerbert Xu scatterwalk_start(&walk, sg); 1145fa0fea2SHerbert Xu 1155fa0fea2SHerbert Xu if (start < offset + sg->length) 1165fa0fea2SHerbert Xu break; 1175fa0fea2SHerbert Xu 1185fa0fea2SHerbert Xu offset += sg->length; 119b2ab4a57SHerbert Xu sg = scatterwalk_sg_next(sg); 1205fa0fea2SHerbert Xu } 1215fa0fea2SHerbert Xu 1225fa0fea2SHerbert Xu scatterwalk_advance(&walk, start - offset); 1235fa0fea2SHerbert Xu scatterwalk_copychunks(buf, &walk, nbytes, out); 1245fa0fea2SHerbert Xu scatterwalk_done(&walk, out, 0); 1255fa0fea2SHerbert Xu } 1265fa0fea2SHerbert Xu EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy); 127257aff51SJoel Fernandes 128257aff51SJoel Fernandes int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes) 129257aff51SJoel Fernandes { 130257aff51SJoel Fernandes int offset = 0, n = 0; 131257aff51SJoel Fernandes 132257aff51SJoel Fernandes /* num_bytes is too small */ 133257aff51SJoel Fernandes if (num_bytes < sg->length) 134257aff51SJoel Fernandes return -1; 135257aff51SJoel Fernandes 136257aff51SJoel Fernandes do { 137257aff51SJoel Fernandes offset += sg->length; 138257aff51SJoel Fernandes n++; 139257aff51SJoel Fernandes sg = scatterwalk_sg_next(sg); 140257aff51SJoel Fernandes 141257aff51SJoel Fernandes /* num_bytes is too large */ 142257aff51SJoel Fernandes if (unlikely(!sg && (num_bytes < offset))) 143257aff51SJoel Fernandes return -1; 144257aff51SJoel Fernandes } while (sg && (num_bytes > offset)); 145257aff51SJoel Fernandes 146257aff51SJoel Fernandes return n; 147257aff51SJoel Fernandes } 148257aff51SJoel Fernandes EXPORT_SYMBOL_GPL(scatterwalk_bytes_sglen); 149