xref: /linux/arch/powerpc/lib/checksum_wrappers.c (revision 1a59d1b8e05ea6ab45f7e18897de1ef0e6bc3da6)
1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
203bc8b0fSChristophe Leroy /*
303bc8b0fSChristophe Leroy  *
403bc8b0fSChristophe Leroy  * Copyright (C) IBM Corporation, 2010
503bc8b0fSChristophe Leroy  *
603bc8b0fSChristophe Leroy  * Author: Anton Blanchard <anton@au.ibm.com>
703bc8b0fSChristophe Leroy  */
803bc8b0fSChristophe Leroy #include <linux/export.h>
903bc8b0fSChristophe Leroy #include <linux/compiler.h>
1003bc8b0fSChristophe Leroy #include <linux/types.h>
1103bc8b0fSChristophe Leroy #include <asm/checksum.h>
127c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
1303bc8b0fSChristophe Leroy 
1403bc8b0fSChristophe Leroy __wsum csum_and_copy_from_user(const void __user *src, void *dst,
1503bc8b0fSChristophe Leroy 			       int len, __wsum sum, int *err_ptr)
1603bc8b0fSChristophe Leroy {
1703bc8b0fSChristophe Leroy 	unsigned int csum;
1803bc8b0fSChristophe Leroy 
1903bc8b0fSChristophe Leroy 	might_sleep();
20de78a9c4SChristophe Leroy 	allow_read_from_user(src, len);
2103bc8b0fSChristophe Leroy 
2203bc8b0fSChristophe Leroy 	*err_ptr = 0;
2303bc8b0fSChristophe Leroy 
2403bc8b0fSChristophe Leroy 	if (!len) {
2503bc8b0fSChristophe Leroy 		csum = 0;
2603bc8b0fSChristophe Leroy 		goto out;
2703bc8b0fSChristophe Leroy 	}
2803bc8b0fSChristophe Leroy 
2996d4f267SLinus Torvalds 	if (unlikely((len < 0) || !access_ok(src, len))) {
3003bc8b0fSChristophe Leroy 		*err_ptr = -EFAULT;
3103bc8b0fSChristophe Leroy 		csum = (__force unsigned int)sum;
3203bc8b0fSChristophe Leroy 		goto out;
3303bc8b0fSChristophe Leroy 	}
3403bc8b0fSChristophe Leroy 
3503bc8b0fSChristophe Leroy 	csum = csum_partial_copy_generic((void __force *)src, dst,
3603bc8b0fSChristophe Leroy 					 len, sum, err_ptr, NULL);
3703bc8b0fSChristophe Leroy 
3803bc8b0fSChristophe Leroy 	if (unlikely(*err_ptr)) {
3903bc8b0fSChristophe Leroy 		int missing = __copy_from_user(dst, src, len);
4003bc8b0fSChristophe Leroy 
4103bc8b0fSChristophe Leroy 		if (missing) {
4203bc8b0fSChristophe Leroy 			memset(dst + len - missing, 0, missing);
4303bc8b0fSChristophe Leroy 			*err_ptr = -EFAULT;
4403bc8b0fSChristophe Leroy 		} else {
4503bc8b0fSChristophe Leroy 			*err_ptr = 0;
4603bc8b0fSChristophe Leroy 		}
4703bc8b0fSChristophe Leroy 
4803bc8b0fSChristophe Leroy 		csum = csum_partial(dst, len, sum);
4903bc8b0fSChristophe Leroy 	}
5003bc8b0fSChristophe Leroy 
5103bc8b0fSChristophe Leroy out:
52de78a9c4SChristophe Leroy 	prevent_read_from_user(src, len);
5303bc8b0fSChristophe Leroy 	return (__force __wsum)csum;
5403bc8b0fSChristophe Leroy }
5503bc8b0fSChristophe Leroy EXPORT_SYMBOL(csum_and_copy_from_user);
5603bc8b0fSChristophe Leroy 
5703bc8b0fSChristophe Leroy __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
5803bc8b0fSChristophe Leroy 			     __wsum sum, int *err_ptr)
5903bc8b0fSChristophe Leroy {
6003bc8b0fSChristophe Leroy 	unsigned int csum;
6103bc8b0fSChristophe Leroy 
6203bc8b0fSChristophe Leroy 	might_sleep();
63de78a9c4SChristophe Leroy 	allow_write_to_user(dst, len);
6403bc8b0fSChristophe Leroy 
6503bc8b0fSChristophe Leroy 	*err_ptr = 0;
6603bc8b0fSChristophe Leroy 
6703bc8b0fSChristophe Leroy 	if (!len) {
6803bc8b0fSChristophe Leroy 		csum = 0;
6903bc8b0fSChristophe Leroy 		goto out;
7003bc8b0fSChristophe Leroy 	}
7103bc8b0fSChristophe Leroy 
7296d4f267SLinus Torvalds 	if (unlikely((len < 0) || !access_ok(dst, len))) {
7303bc8b0fSChristophe Leroy 		*err_ptr = -EFAULT;
7403bc8b0fSChristophe Leroy 		csum = -1; /* invalid checksum */
7503bc8b0fSChristophe Leroy 		goto out;
7603bc8b0fSChristophe Leroy 	}
7703bc8b0fSChristophe Leroy 
7803bc8b0fSChristophe Leroy 	csum = csum_partial_copy_generic(src, (void __force *)dst,
7903bc8b0fSChristophe Leroy 					 len, sum, NULL, err_ptr);
8003bc8b0fSChristophe Leroy 
8103bc8b0fSChristophe Leroy 	if (unlikely(*err_ptr)) {
8203bc8b0fSChristophe Leroy 		csum = csum_partial(src, len, sum);
8303bc8b0fSChristophe Leroy 
8403bc8b0fSChristophe Leroy 		if (copy_to_user(dst, src, len)) {
8503bc8b0fSChristophe Leroy 			*err_ptr = -EFAULT;
8603bc8b0fSChristophe Leroy 			csum = -1; /* invalid checksum */
8703bc8b0fSChristophe Leroy 		}
8803bc8b0fSChristophe Leroy 	}
8903bc8b0fSChristophe Leroy 
9003bc8b0fSChristophe Leroy out:
91de78a9c4SChristophe Leroy 	prevent_write_to_user(dst, len);
9203bc8b0fSChristophe Leroy 	return (__force __wsum)csum;
9303bc8b0fSChristophe Leroy }
9403bc8b0fSChristophe Leroy EXPORT_SYMBOL(csum_and_copy_to_user);
95