1*0febaae0SAlexandru Elisei /*
2*0febaae0SAlexandru Elisei * Taken from Linux kernel version v5.15.
3*0febaae0SAlexandru Elisei */
4*0febaae0SAlexandru Elisei #include <ctype.h>
5*0febaae0SAlexandru Elisei #include <limits.h>
6*0febaae0SAlexandru Elisei #include <stdlib.h>
7*0febaae0SAlexandru Elisei #include <strings.h>
8*0febaae0SAlexandru Elisei
9*0febaae0SAlexandru Elisei #include "linux/bitmap.h"
10*0febaae0SAlexandru Elisei #include "linux/bitops.h"
11*0febaae0SAlexandru Elisei #include "linux/err.h"
12*0febaae0SAlexandru Elisei
13*0febaae0SAlexandru Elisei /*
14*0febaae0SAlexandru Elisei * Region 9-38:4/10 describes the following bitmap structure:
15*0febaae0SAlexandru Elisei * 0 9 12 18 38 N
16*0febaae0SAlexandru Elisei * .........****......****......****..................
17*0febaae0SAlexandru Elisei * ^ ^ ^ ^ ^
18*0febaae0SAlexandru Elisei * start off group_len end nbits
19*0febaae0SAlexandru Elisei */
20*0febaae0SAlexandru Elisei struct region {
21*0febaae0SAlexandru Elisei unsigned int start;
22*0febaae0SAlexandru Elisei unsigned int off;
23*0febaae0SAlexandru Elisei unsigned int group_len;
24*0febaae0SAlexandru Elisei unsigned int end;
25*0febaae0SAlexandru Elisei unsigned int nbits;
26*0febaae0SAlexandru Elisei };
27*0febaae0SAlexandru Elisei
__bitmap_set(unsigned long * map,unsigned int start,int len)28*0febaae0SAlexandru Elisei void __bitmap_set(unsigned long *map, unsigned int start, int len)
29*0febaae0SAlexandru Elisei {
30*0febaae0SAlexandru Elisei unsigned long *p = map + BIT_WORD(start);
31*0febaae0SAlexandru Elisei const unsigned int size = start + len;
32*0febaae0SAlexandru Elisei int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
33*0febaae0SAlexandru Elisei unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
34*0febaae0SAlexandru Elisei
35*0febaae0SAlexandru Elisei while (len - bits_to_set >= 0) {
36*0febaae0SAlexandru Elisei *p |= mask_to_set;
37*0febaae0SAlexandru Elisei len -= bits_to_set;
38*0febaae0SAlexandru Elisei bits_to_set = BITS_PER_LONG;
39*0febaae0SAlexandru Elisei mask_to_set = ~0UL;
40*0febaae0SAlexandru Elisei p++;
41*0febaae0SAlexandru Elisei }
42*0febaae0SAlexandru Elisei if (len) {
43*0febaae0SAlexandru Elisei mask_to_set &= BITMAP_LAST_WORD_MASK(size);
44*0febaae0SAlexandru Elisei *p |= mask_to_set;
45*0febaae0SAlexandru Elisei }
46*0febaae0SAlexandru Elisei }
47*0febaae0SAlexandru Elisei
bitmap_set_region(const struct region * r,unsigned long * bitmap)48*0febaae0SAlexandru Elisei static void bitmap_set_region(const struct region *r, unsigned long *bitmap)
49*0febaae0SAlexandru Elisei {
50*0febaae0SAlexandru Elisei unsigned int start;
51*0febaae0SAlexandru Elisei
52*0febaae0SAlexandru Elisei for (start = r->start; start <= r->end; start += r->group_len)
53*0febaae0SAlexandru Elisei bitmap_set(bitmap, start, min(r->end - start + 1, r->off));
54*0febaae0SAlexandru Elisei }
55*0febaae0SAlexandru Elisei
__end_of_region(char c)56*0febaae0SAlexandru Elisei static inline bool __end_of_region(char c)
57*0febaae0SAlexandru Elisei {
58*0febaae0SAlexandru Elisei return isspace(c) || c == ',';
59*0febaae0SAlexandru Elisei }
60*0febaae0SAlexandru Elisei
end_of_str(char c)61*0febaae0SAlexandru Elisei static inline bool end_of_str(char c)
62*0febaae0SAlexandru Elisei {
63*0febaae0SAlexandru Elisei return c == '\0' || c == '\n';
64*0febaae0SAlexandru Elisei }
65*0febaae0SAlexandru Elisei
end_of_region(char c)66*0febaae0SAlexandru Elisei static inline bool end_of_region(char c)
67*0febaae0SAlexandru Elisei {
68*0febaae0SAlexandru Elisei return __end_of_region(c) || end_of_str(c);
69*0febaae0SAlexandru Elisei }
70*0febaae0SAlexandru Elisei
71*0febaae0SAlexandru Elisei /*
72*0febaae0SAlexandru Elisei * The format allows commas and whitespaces at the beginning
73*0febaae0SAlexandru Elisei * of the region.
74*0febaae0SAlexandru Elisei */
bitmap_find_region(const char * str)75*0febaae0SAlexandru Elisei static const char *bitmap_find_region(const char *str)
76*0febaae0SAlexandru Elisei {
77*0febaae0SAlexandru Elisei while (__end_of_region(*str))
78*0febaae0SAlexandru Elisei str++;
79*0febaae0SAlexandru Elisei
80*0febaae0SAlexandru Elisei return end_of_str(*str) ? NULL : str;
81*0febaae0SAlexandru Elisei }
82*0febaae0SAlexandru Elisei
bitmap_check_region(const struct region * r)83*0febaae0SAlexandru Elisei static int bitmap_check_region(const struct region *r)
84*0febaae0SAlexandru Elisei {
85*0febaae0SAlexandru Elisei if (r->start > r->end || r->group_len == 0 || r->off > r->group_len)
86*0febaae0SAlexandru Elisei return -EINVAL;
87*0febaae0SAlexandru Elisei
88*0febaae0SAlexandru Elisei if (r->end >= r->nbits)
89*0febaae0SAlexandru Elisei return -ERANGE;
90*0febaae0SAlexandru Elisei
91*0febaae0SAlexandru Elisei return 0;
92*0febaae0SAlexandru Elisei }
93*0febaae0SAlexandru Elisei
bitmap_getnum(const char * str,unsigned int * num,unsigned int lastbit)94*0febaae0SAlexandru Elisei static const char *bitmap_getnum(const char *str, unsigned int *num,
95*0febaae0SAlexandru Elisei unsigned int lastbit)
96*0febaae0SAlexandru Elisei {
97*0febaae0SAlexandru Elisei unsigned long long n;
98*0febaae0SAlexandru Elisei char *endptr;
99*0febaae0SAlexandru Elisei
100*0febaae0SAlexandru Elisei if (str[0] == 'N') {
101*0febaae0SAlexandru Elisei *num = lastbit;
102*0febaae0SAlexandru Elisei return str + 1;
103*0febaae0SAlexandru Elisei }
104*0febaae0SAlexandru Elisei
105*0febaae0SAlexandru Elisei n = strtoll(str, &endptr, 10);
106*0febaae0SAlexandru Elisei /* No digits found. */
107*0febaae0SAlexandru Elisei if (n == 0 && endptr == str)
108*0febaae0SAlexandru Elisei return ERR_PTR(-EINVAL);
109*0febaae0SAlexandru Elisei /* Check for overflows and negative numbers. */
110*0febaae0SAlexandru Elisei if (n == ULLONG_MAX || n != (unsigned long)n || n != (unsigned int)n)
111*0febaae0SAlexandru Elisei return ERR_PTR(-EOVERFLOW);
112*0febaae0SAlexandru Elisei
113*0febaae0SAlexandru Elisei *num = n;
114*0febaae0SAlexandru Elisei return endptr;
115*0febaae0SAlexandru Elisei }
116*0febaae0SAlexandru Elisei
bitmap_parse_region(const char * str,struct region * r)117*0febaae0SAlexandru Elisei static const char *bitmap_parse_region(const char *str, struct region *r)
118*0febaae0SAlexandru Elisei {
119*0febaae0SAlexandru Elisei unsigned int lastbit = r->nbits - 1;
120*0febaae0SAlexandru Elisei
121*0febaae0SAlexandru Elisei if (!strncasecmp(str, "all", 3)) {
122*0febaae0SAlexandru Elisei r->start = 0;
123*0febaae0SAlexandru Elisei r->end = lastbit;
124*0febaae0SAlexandru Elisei str += 3;
125*0febaae0SAlexandru Elisei
126*0febaae0SAlexandru Elisei goto check_pattern;
127*0febaae0SAlexandru Elisei }
128*0febaae0SAlexandru Elisei
129*0febaae0SAlexandru Elisei str = bitmap_getnum(str, &r->start, lastbit);
130*0febaae0SAlexandru Elisei if (IS_ERR(str))
131*0febaae0SAlexandru Elisei return str;
132*0febaae0SAlexandru Elisei
133*0febaae0SAlexandru Elisei if (end_of_region(*str))
134*0febaae0SAlexandru Elisei goto no_end;
135*0febaae0SAlexandru Elisei
136*0febaae0SAlexandru Elisei if (*str != '-')
137*0febaae0SAlexandru Elisei return ERR_PTR(-EINVAL);
138*0febaae0SAlexandru Elisei
139*0febaae0SAlexandru Elisei str = bitmap_getnum(str + 1, &r->end, lastbit);
140*0febaae0SAlexandru Elisei if (IS_ERR(str))
141*0febaae0SAlexandru Elisei return str;
142*0febaae0SAlexandru Elisei
143*0febaae0SAlexandru Elisei check_pattern:
144*0febaae0SAlexandru Elisei if (end_of_region(*str))
145*0febaae0SAlexandru Elisei goto no_pattern;
146*0febaae0SAlexandru Elisei
147*0febaae0SAlexandru Elisei if (*str != ':')
148*0febaae0SAlexandru Elisei return ERR_PTR(-EINVAL);
149*0febaae0SAlexandru Elisei
150*0febaae0SAlexandru Elisei str = bitmap_getnum(str + 1, &r->off, lastbit);
151*0febaae0SAlexandru Elisei if (IS_ERR(str))
152*0febaae0SAlexandru Elisei return str;
153*0febaae0SAlexandru Elisei
154*0febaae0SAlexandru Elisei if (*str != '/')
155*0febaae0SAlexandru Elisei return ERR_PTR(-EINVAL);
156*0febaae0SAlexandru Elisei
157*0febaae0SAlexandru Elisei return bitmap_getnum(str + 1, &r->group_len, lastbit);
158*0febaae0SAlexandru Elisei
159*0febaae0SAlexandru Elisei no_end:
160*0febaae0SAlexandru Elisei r->end = r->start;
161*0febaae0SAlexandru Elisei no_pattern:
162*0febaae0SAlexandru Elisei r->off = r->end + 1;
163*0febaae0SAlexandru Elisei r->group_len = r->end + 1;
164*0febaae0SAlexandru Elisei
165*0febaae0SAlexandru Elisei return end_of_str(*str) ? NULL : str;
166*0febaae0SAlexandru Elisei }
167*0febaae0SAlexandru Elisei
168*0febaae0SAlexandru Elisei /**
169*0febaae0SAlexandru Elisei * bitmap_parselist - convert list format ASCII string to bitmap
170*0febaae0SAlexandru Elisei * @buf: read user string from this buffer; must be terminated
171*0febaae0SAlexandru Elisei * with a \0 or \n.
172*0febaae0SAlexandru Elisei * @maskp: write resulting mask here
173*0febaae0SAlexandru Elisei * @nmaskbits: number of bits in mask to be written
174*0febaae0SAlexandru Elisei *
175*0febaae0SAlexandru Elisei * Input format is a comma-separated list of decimal numbers and
176*0febaae0SAlexandru Elisei * ranges. Consecutively set bits are shown as two hyphen-separated
177*0febaae0SAlexandru Elisei * decimal numbers, the smallest and largest bit numbers set in
178*0febaae0SAlexandru Elisei * the range.
179*0febaae0SAlexandru Elisei * Optionally each range can be postfixed to denote that only parts of it
180*0febaae0SAlexandru Elisei * should be set. The range will divided to groups of specific size.
181*0febaae0SAlexandru Elisei * From each group will be used only defined amount of bits.
182*0febaae0SAlexandru Elisei * Syntax: range:used_size/group_size
183*0febaae0SAlexandru Elisei * Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769
184*0febaae0SAlexandru Elisei * The value 'N' can be used as a dynamically substituted token for the
185*0febaae0SAlexandru Elisei * maximum allowed value; i.e (nmaskbits - 1). Keep in mind that it is
186*0febaae0SAlexandru Elisei * dynamic, so if system changes cause the bitmap width to change, such
187*0febaae0SAlexandru Elisei * as more cores in a CPU list, then any ranges using N will also change.
188*0febaae0SAlexandru Elisei *
189*0febaae0SAlexandru Elisei * Returns: 0 on success, -errno on invalid input strings. Error values:
190*0febaae0SAlexandru Elisei *
191*0febaae0SAlexandru Elisei * - ``-EINVAL``: wrong region format
192*0febaae0SAlexandru Elisei * - ``-EINVAL``: invalid character in string
193*0febaae0SAlexandru Elisei * - ``-ERANGE``: bit number specified too large for mask
194*0febaae0SAlexandru Elisei * - ``-EOVERFLOW``: integer overflow in the input parameters
195*0febaae0SAlexandru Elisei */
bitmap_parselist(const char * buf,unsigned long * maskp,int nmaskbits)196*0febaae0SAlexandru Elisei int bitmap_parselist(const char *buf, unsigned long *maskp, int nmaskbits)
197*0febaae0SAlexandru Elisei {
198*0febaae0SAlexandru Elisei struct region r;
199*0febaae0SAlexandru Elisei long ret;
200*0febaae0SAlexandru Elisei
201*0febaae0SAlexandru Elisei r.nbits = nmaskbits;
202*0febaae0SAlexandru Elisei bitmap_zero(maskp, r.nbits);
203*0febaae0SAlexandru Elisei
204*0febaae0SAlexandru Elisei while (buf) {
205*0febaae0SAlexandru Elisei buf = bitmap_find_region(buf);
206*0febaae0SAlexandru Elisei if (buf == NULL)
207*0febaae0SAlexandru Elisei return 0;
208*0febaae0SAlexandru Elisei
209*0febaae0SAlexandru Elisei buf = bitmap_parse_region(buf, &r);
210*0febaae0SAlexandru Elisei if (IS_ERR(buf))
211*0febaae0SAlexandru Elisei return PTR_ERR(buf);
212*0febaae0SAlexandru Elisei
213*0febaae0SAlexandru Elisei ret = bitmap_check_region(&r);
214*0febaae0SAlexandru Elisei if (ret)
215*0febaae0SAlexandru Elisei return ret;
216*0febaae0SAlexandru Elisei
217*0febaae0SAlexandru Elisei bitmap_set_region(&r, maskp);
218*0febaae0SAlexandru Elisei }
219*0febaae0SAlexandru Elisei
220*0febaae0SAlexandru Elisei return 0;
221*0febaae0SAlexandru Elisei }
222*0febaae0SAlexandru Elisei
__bitmap_and(unsigned long * dst,const unsigned long * src1,const unsigned long * src2,unsigned int nbits)223*0febaae0SAlexandru Elisei bool __bitmap_and(unsigned long *dst, const unsigned long *src1,
224*0febaae0SAlexandru Elisei const unsigned long *src2, unsigned int nbits)
225*0febaae0SAlexandru Elisei {
226*0febaae0SAlexandru Elisei unsigned int lim = nbits / BITS_PER_LONG;
227*0febaae0SAlexandru Elisei unsigned long result = 0;
228*0febaae0SAlexandru Elisei unsigned int k;
229*0febaae0SAlexandru Elisei
230*0febaae0SAlexandru Elisei for (k = 0; k < lim; k++)
231*0febaae0SAlexandru Elisei result |= (dst[k] = src1[k] & src2[k]);
232*0febaae0SAlexandru Elisei
233*0febaae0SAlexandru Elisei if (nbits % BITS_PER_LONG) {
234*0febaae0SAlexandru Elisei result |= (dst[k] = src1[k] & src2[k] &
235*0febaae0SAlexandru Elisei BITMAP_LAST_WORD_MASK(nbits));
236*0febaae0SAlexandru Elisei }
237*0febaae0SAlexandru Elisei
238*0febaae0SAlexandru Elisei return result != 0;
239*0febaae0SAlexandru Elisei }
240*0febaae0SAlexandru Elisei
__bitmap_subset(const unsigned long * bitmap1,const unsigned long * bitmap2,unsigned int nbits)241*0febaae0SAlexandru Elisei bool __bitmap_subset(const unsigned long *bitmap1, const unsigned long *bitmap2,
242*0febaae0SAlexandru Elisei unsigned int nbits)
243*0febaae0SAlexandru Elisei {
244*0febaae0SAlexandru Elisei unsigned int k, lim = nbits / BITS_PER_LONG;
245*0febaae0SAlexandru Elisei
246*0febaae0SAlexandru Elisei for (k = 0; k < lim; k++)
247*0febaae0SAlexandru Elisei if (bitmap1[k] & ~bitmap2[k])
248*0febaae0SAlexandru Elisei return false;
249*0febaae0SAlexandru Elisei
250*0febaae0SAlexandru Elisei if (nbits % BITS_PER_LONG) {
251*0febaae0SAlexandru Elisei if ((bitmap1[k] & ~bitmap2[k]) & BITMAP_LAST_WORD_MASK(nbits))
252*0febaae0SAlexandru Elisei return false;
253*0febaae0SAlexandru Elisei }
254*0febaae0SAlexandru Elisei
255*0febaae0SAlexandru Elisei return true;
256*0febaae0SAlexandru Elisei }
257