| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | 
|---|
| 2 | #ifndef _ASM_WORD_AT_A_TIME_H | 
|---|
| 3 | #define _ASM_WORD_AT_A_TIME_H | 
|---|
| 4 |  | 
|---|
| 5 | #include <linux/bitops.h> | 
|---|
| 6 | #include <linux/wordpart.h> | 
|---|
| 7 |  | 
|---|
| 8 | struct word_at_a_time { | 
|---|
| 9 | const unsigned long one_bits, high_bits; | 
|---|
| 10 | }; | 
|---|
| 11 |  | 
|---|
| 12 | #define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) } | 
|---|
| 13 |  | 
|---|
| 14 | /* Return nonzero if it has a zero */ | 
|---|
| 15 | static inline unsigned long has_zero(unsigned long a, unsigned long *bits, const struct word_at_a_time *c) | 
|---|
| 16 | { | 
|---|
| 17 | unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits; | 
|---|
| 18 | *bits = mask; | 
|---|
| 19 | return mask; | 
|---|
| 20 | } | 
|---|
| 21 |  | 
|---|
| 22 | static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits, const struct word_at_a_time *c) | 
|---|
| 23 | { | 
|---|
| 24 | return bits; | 
|---|
| 25 | } | 
|---|
| 26 |  | 
|---|
| 27 | #ifdef CONFIG_64BIT | 
|---|
| 28 |  | 
|---|
| 29 | /* Keep the initial has_zero() value for both bitmask and size calc */ | 
|---|
| 30 | #define create_zero_mask(bits) (bits) | 
|---|
| 31 |  | 
|---|
| 32 | static inline unsigned long zero_bytemask(unsigned long bits) | 
|---|
| 33 | { | 
|---|
| 34 | bits = (bits - 1) & ~bits; | 
|---|
| 35 | return bits >> 7; | 
|---|
| 36 | } | 
|---|
| 37 |  | 
|---|
| 38 | #define find_zero(bits) (__ffs(bits) >> 3) | 
|---|
| 39 |  | 
|---|
| 40 | #else | 
|---|
| 41 |  | 
|---|
| 42 | /* Create the final mask for both bytemask and size */ | 
|---|
| 43 | static inline unsigned long create_zero_mask(unsigned long bits) | 
|---|
| 44 | { | 
|---|
| 45 | bits = (bits - 1) & ~bits; | 
|---|
| 46 | return bits >> 7; | 
|---|
| 47 | } | 
|---|
| 48 |  | 
|---|
| 49 | /* The mask we created is directly usable as a bytemask */ | 
|---|
| 50 | #define zero_bytemask(mask) (mask) | 
|---|
| 51 |  | 
|---|
| 52 | /* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ | 
|---|
| 53 | static inline unsigned long find_zero(unsigned long mask) | 
|---|
| 54 | { | 
|---|
| 55 | /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */ | 
|---|
| 56 | long a = (0x0ff0001+mask) >> 23; | 
|---|
| 57 | /* Fix the 1 for 00 case */ | 
|---|
| 58 | return a & mask; | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | #endif | 
|---|
| 62 |  | 
|---|
| 63 | /* | 
|---|
| 64 | * Load an unaligned word from kernel space. | 
|---|
| 65 | * | 
|---|
| 66 | * In the (very unlikely) case of the word being a page-crosser | 
|---|
| 67 | * and the next page not being mapped, take the exception and | 
|---|
| 68 | * return zeroes in the non-existing part. | 
|---|
| 69 | */ | 
|---|
| 70 | static inline unsigned long load_unaligned_zeropad(const void *addr) | 
|---|
| 71 | { | 
|---|
| 72 | unsigned long ret; | 
|---|
| 73 |  | 
|---|
| 74 | asm volatile( | 
|---|
| 75 | "1:	mov %[mem], %[ret]\n" | 
|---|
| 76 | "2:\n" | 
|---|
| 77 | _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_ZEROPAD) | 
|---|
| 78 | : [ret] "=r"(ret) | 
|---|
| 79 | : [mem] "m"(*(unsigned long *)addr)); | 
|---|
| 80 |  | 
|---|
| 81 | return ret; | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 | #endif /* _ASM_WORD_AT_A_TIME_H */ | 
|---|
| 85 |  | 
|---|