18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef _ASM_WORD_AT_A_TIME_H 38c2ecf20Sopenharmony_ci#define _ASM_WORD_AT_A_TIME_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * This is largely generic for little-endian machines, but the 98c2ecf20Sopenharmony_ci * optimal byte mask counting is probably going to be something 108c2ecf20Sopenharmony_ci * that is architecture-specific. If you have a reliably fast 118c2ecf20Sopenharmony_ci * bit count instruction, that might be better than the multiply 128c2ecf20Sopenharmony_ci * and shift, for example. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_cistruct word_at_a_time { 158c2ecf20Sopenharmony_ci const unsigned long one_bits, high_bits; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) } 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Jan Achrenius on G+: microoptimized version of 248c2ecf20Sopenharmony_ci * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56" 258c2ecf20Sopenharmony_ci * that works for the bytemasks without having to 268c2ecf20Sopenharmony_ci * mask them first. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic inline long count_masked_bytes(unsigned long mask) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return mask*0x0001020304050608ul >> 56; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#else /* 32-bit case */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ 368c2ecf20Sopenharmony_cistatic inline long count_masked_bytes(long mask) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */ 398c2ecf20Sopenharmony_ci long a = (0x0ff0001+mask) >> 23; 408c2ecf20Sopenharmony_ci /* Fix the 1 for 00 case */ 418c2ecf20Sopenharmony_ci return a & mask; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#endif 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* Return nonzero if it has a zero */ 478c2ecf20Sopenharmony_cistatic inline unsigned long has_zero(unsigned long a, unsigned long *bits, const struct word_at_a_time *c) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits; 508c2ecf20Sopenharmony_ci *bits = mask; 518c2ecf20Sopenharmony_ci return mask; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits, const struct word_at_a_time *c) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return bits; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline unsigned long create_zero_mask(unsigned long bits) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci bits = (bits - 1) & ~bits; 628c2ecf20Sopenharmony_ci return bits >> 7; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* The mask we created is directly usable as a bytemask */ 668c2ecf20Sopenharmony_ci#define zero_bytemask(mask) (mask) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline unsigned long find_zero(unsigned long mask) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return count_masked_bytes(mask); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * Load an unaligned word from kernel space. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * In the (very unlikely) case of the word being a page-crosser 778c2ecf20Sopenharmony_ci * and the next page not being mapped, take the exception and 788c2ecf20Sopenharmony_ci * return zeroes in the non-existing part. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic inline unsigned long load_unaligned_zeropad(const void *addr) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci unsigned long ret, dummy; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci asm( 858c2ecf20Sopenharmony_ci "1:\tmov %2,%0\n" 868c2ecf20Sopenharmony_ci "2:\n" 878c2ecf20Sopenharmony_ci ".section .fixup,\"ax\"\n" 888c2ecf20Sopenharmony_ci "3:\t" 898c2ecf20Sopenharmony_ci "lea %2,%1\n\t" 908c2ecf20Sopenharmony_ci "and %3,%1\n\t" 918c2ecf20Sopenharmony_ci "mov (%1),%0\n\t" 928c2ecf20Sopenharmony_ci "leal %2,%%ecx\n\t" 938c2ecf20Sopenharmony_ci "andl %4,%%ecx\n\t" 948c2ecf20Sopenharmony_ci "shll $3,%%ecx\n\t" 958c2ecf20Sopenharmony_ci "shr %%cl,%0\n\t" 968c2ecf20Sopenharmony_ci "jmp 2b\n" 978c2ecf20Sopenharmony_ci ".previous\n" 988c2ecf20Sopenharmony_ci _ASM_EXTABLE(1b, 3b) 998c2ecf20Sopenharmony_ci :"=&r" (ret),"=&c" (dummy) 1008c2ecf20Sopenharmony_ci :"m" (*(unsigned long *)addr), 1018c2ecf20Sopenharmony_ci "i" (-sizeof(unsigned long)), 1028c2ecf20Sopenharmony_ci "i" (sizeof(unsigned long)-1)); 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#endif /* _ASM_WORD_AT_A_TIME_H */ 107