162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cryptographic API.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Cipher operations.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
862306a36Sopenharmony_ci *               2002 Adam J. Richter <adam@yggdrasil.com>
962306a36Sopenharmony_ci *               2004 Jean-Luc Cooke <jlcooke@certainkey.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <crypto/scatterwalk.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/scatterlist.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic inline void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	void *src = out ? buf : sgdata;
2162306a36Sopenharmony_ci	void *dst = out ? sgdata : buf;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	memcpy(dst, src, nbytes);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid scatterwalk_copychunks(void *buf, struct scatter_walk *walk,
2762306a36Sopenharmony_ci			    size_t nbytes, int out)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	for (;;) {
3062306a36Sopenharmony_ci		unsigned int len_this_page = scatterwalk_pagelen(walk);
3162306a36Sopenharmony_ci		u8 *vaddr;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		if (len_this_page > nbytes)
3462306a36Sopenharmony_ci			len_this_page = nbytes;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		if (out != 2) {
3762306a36Sopenharmony_ci			vaddr = scatterwalk_map(walk);
3862306a36Sopenharmony_ci			memcpy_dir(buf, vaddr, len_this_page, out);
3962306a36Sopenharmony_ci			scatterwalk_unmap(vaddr);
4062306a36Sopenharmony_ci		}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		scatterwalk_advance(walk, len_this_page);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		if (nbytes == len_this_page)
4562306a36Sopenharmony_ci			break;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		buf += len_this_page;
4862306a36Sopenharmony_ci		nbytes -= len_this_page;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		scatterwalk_pagedone(walk, out & 1, 1);
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scatterwalk_copychunks);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_civoid scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
5662306a36Sopenharmony_ci			      unsigned int start, unsigned int nbytes, int out)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct scatter_walk walk;
5962306a36Sopenharmony_ci	struct scatterlist tmp[2];
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!nbytes)
6262306a36Sopenharmony_ci		return;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	sg = scatterwalk_ffwd(tmp, sg, start);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	scatterwalk_start(&walk, sg);
6762306a36Sopenharmony_ci	scatterwalk_copychunks(buf, &walk, nbytes, out);
6862306a36Sopenharmony_ci	scatterwalk_done(&walk, out, 0);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scatterwalk_map_and_copy);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
7362306a36Sopenharmony_ci				     struct scatterlist *src,
7462306a36Sopenharmony_ci				     unsigned int len)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	for (;;) {
7762306a36Sopenharmony_ci		if (!len)
7862306a36Sopenharmony_ci			return src;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (src->length > len)
8162306a36Sopenharmony_ci			break;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		len -= src->length;
8462306a36Sopenharmony_ci		src = sg_next(src);
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	sg_init_table(dst, 2);
8862306a36Sopenharmony_ci	sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
8962306a36Sopenharmony_ci	scatterwalk_crypto_chain(dst, sg_next(src), 2);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return dst;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scatterwalk_ffwd);
94