162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 ARM Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#ifndef __ASM_WORD_AT_A_TIME_H
662306a36Sopenharmony_ci#define __ASM_WORD_AT_A_TIME_H
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/uaccess.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#ifndef __AARCH64EB__
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct word_at_a_time {
1562306a36Sopenharmony_ci	const unsigned long one_bits, high_bits;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic inline unsigned long has_zero(unsigned long a, unsigned long *bits,
2162306a36Sopenharmony_ci				     const struct word_at_a_time *c)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits;
2462306a36Sopenharmony_ci	*bits = mask;
2562306a36Sopenharmony_ci	return mask;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define prep_zero_mask(a, bits, c) (bits)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic inline unsigned long create_zero_mask(unsigned long bits)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	bits = (bits - 1) & ~bits;
3362306a36Sopenharmony_ci	return bits >> 7;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic inline unsigned long find_zero(unsigned long mask)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return fls64(mask) >> 3;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define zero_bytemask(mask) (mask)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#else	/* __AARCH64EB__ */
4462306a36Sopenharmony_ci#include <asm-generic/word-at-a-time.h>
4562306a36Sopenharmony_ci#endif
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Load an unaligned word from kernel space.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * In the (very unlikely) case of the word being a page-crosser
5162306a36Sopenharmony_ci * and the next page not being mapped, take the exception and
5262306a36Sopenharmony_ci * return zeroes in the non-existing part.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic inline unsigned long load_unaligned_zeropad(const void *addr)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned long ret;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	__mte_enable_tco_async();
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Load word from unaligned pointer addr */
6162306a36Sopenharmony_ci	asm(
6262306a36Sopenharmony_ci	"1:	ldr	%0, %2\n"
6362306a36Sopenharmony_ci	"2:\n"
6462306a36Sopenharmony_ci	_ASM_EXTABLE_LOAD_UNALIGNED_ZEROPAD(1b, 2b, %0, %1)
6562306a36Sopenharmony_ci	: "=&r" (ret)
6662306a36Sopenharmony_ci	: "r" (addr), "Q" (*(unsigned long *)addr));
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	__mte_disable_tco_async();
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#endif /* __ASM_WORD_AT_A_TIME_H */
74