xref: /openbmc/linux/lib/kstrtox.c (revision 75f25bd3)
1 /*
2  * Convert integer string representation to an integer.
3  * If an integer doesn't fit into specified type, -E is returned.
4  *
5  * Integer starts with optional sign.
6  * kstrtou*() functions do not accept sign "-".
7  *
8  * Radix 0 means autodetection: leading "0x" implies radix 16,
9  * leading "0" implies radix 8, otherwise radix is 10.
10  * Autodetection hints work after optional sign, but not before.
11  *
12  * If -E is returned, result is not touched.
13  */
14 #include <linux/ctype.h>
15 #include <linux/errno.h>
16 #include <linux/kernel.h>
17 #include <linux/math64.h>
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <asm/uaccess.h>
21 
22 static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
23 {
24 	unsigned long long acc;
25 	int ok;
26 
27 	if (base == 0) {
28 		if (s[0] == '0') {
29 			if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
30 				base = 16;
31 			else
32 				base = 8;
33 		} else
34 			base = 10;
35 	}
36 	if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
37 		s += 2;
38 
39 	acc = 0;
40 	ok = 0;
41 	while (*s) {
42 		unsigned int val;
43 
44 		if ('0' <= *s && *s <= '9')
45 			val = *s - '0';
46 		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
47 			val = _tolower(*s) - 'a' + 10;
48 		else if (*s == '\n' && *(s + 1) == '\0')
49 			break;
50 		else
51 			return -EINVAL;
52 
53 		if (val >= base)
54 			return -EINVAL;
55 		if (acc > div_u64(ULLONG_MAX - val, base))
56 			return -ERANGE;
57 		acc = acc * base + val;
58 		ok = 1;
59 
60 		s++;
61 	}
62 	if (!ok)
63 		return -EINVAL;
64 	*res = acc;
65 	return 0;
66 }
67 
68 int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
69 {
70 	if (s[0] == '+')
71 		s++;
72 	return _kstrtoull(s, base, res);
73 }
74 EXPORT_SYMBOL(kstrtoull);
75 
76 int kstrtoll(const char *s, unsigned int base, long long *res)
77 {
78 	unsigned long long tmp;
79 	int rv;
80 
81 	if (s[0] == '-') {
82 		rv = _kstrtoull(s + 1, base, &tmp);
83 		if (rv < 0)
84 			return rv;
85 		if ((long long)(-tmp) >= 0)
86 			return -ERANGE;
87 		*res = -tmp;
88 	} else {
89 		rv = kstrtoull(s, base, &tmp);
90 		if (rv < 0)
91 			return rv;
92 		if ((long long)tmp < 0)
93 			return -ERANGE;
94 		*res = tmp;
95 	}
96 	return 0;
97 }
98 EXPORT_SYMBOL(kstrtoll);
99 
100 /* Internal, do not use. */
101 int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
102 {
103 	unsigned long long tmp;
104 	int rv;
105 
106 	rv = kstrtoull(s, base, &tmp);
107 	if (rv < 0)
108 		return rv;
109 	if (tmp != (unsigned long long)(unsigned long)tmp)
110 		return -ERANGE;
111 	*res = tmp;
112 	return 0;
113 }
114 EXPORT_SYMBOL(_kstrtoul);
115 
116 /* Internal, do not use. */
117 int _kstrtol(const char *s, unsigned int base, long *res)
118 {
119 	long long tmp;
120 	int rv;
121 
122 	rv = kstrtoll(s, base, &tmp);
123 	if (rv < 0)
124 		return rv;
125 	if (tmp != (long long)(long)tmp)
126 		return -ERANGE;
127 	*res = tmp;
128 	return 0;
129 }
130 EXPORT_SYMBOL(_kstrtol);
131 
132 int kstrtouint(const char *s, unsigned int base, unsigned int *res)
133 {
134 	unsigned long long tmp;
135 	int rv;
136 
137 	rv = kstrtoull(s, base, &tmp);
138 	if (rv < 0)
139 		return rv;
140 	if (tmp != (unsigned long long)(unsigned int)tmp)
141 		return -ERANGE;
142 	*res = tmp;
143 	return 0;
144 }
145 EXPORT_SYMBOL(kstrtouint);
146 
147 int kstrtoint(const char *s, unsigned int base, int *res)
148 {
149 	long long tmp;
150 	int rv;
151 
152 	rv = kstrtoll(s, base, &tmp);
153 	if (rv < 0)
154 		return rv;
155 	if (tmp != (long long)(int)tmp)
156 		return -ERANGE;
157 	*res = tmp;
158 	return 0;
159 }
160 EXPORT_SYMBOL(kstrtoint);
161 
162 int kstrtou16(const char *s, unsigned int base, u16 *res)
163 {
164 	unsigned long long tmp;
165 	int rv;
166 
167 	rv = kstrtoull(s, base, &tmp);
168 	if (rv < 0)
169 		return rv;
170 	if (tmp != (unsigned long long)(u16)tmp)
171 		return -ERANGE;
172 	*res = tmp;
173 	return 0;
174 }
175 EXPORT_SYMBOL(kstrtou16);
176 
177 int kstrtos16(const char *s, unsigned int base, s16 *res)
178 {
179 	long long tmp;
180 	int rv;
181 
182 	rv = kstrtoll(s, base, &tmp);
183 	if (rv < 0)
184 		return rv;
185 	if (tmp != (long long)(s16)tmp)
186 		return -ERANGE;
187 	*res = tmp;
188 	return 0;
189 }
190 EXPORT_SYMBOL(kstrtos16);
191 
192 int kstrtou8(const char *s, unsigned int base, u8 *res)
193 {
194 	unsigned long long tmp;
195 	int rv;
196 
197 	rv = kstrtoull(s, base, &tmp);
198 	if (rv < 0)
199 		return rv;
200 	if (tmp != (unsigned long long)(u8)tmp)
201 		return -ERANGE;
202 	*res = tmp;
203 	return 0;
204 }
205 EXPORT_SYMBOL(kstrtou8);
206 
207 int kstrtos8(const char *s, unsigned int base, s8 *res)
208 {
209 	long long tmp;
210 	int rv;
211 
212 	rv = kstrtoll(s, base, &tmp);
213 	if (rv < 0)
214 		return rv;
215 	if (tmp != (long long)(s8)tmp)
216 		return -ERANGE;
217 	*res = tmp;
218 	return 0;
219 }
220 EXPORT_SYMBOL(kstrtos8);
221 
222 #define kstrto_from_user(f, g, type)					\
223 int f(const char __user *s, size_t count, unsigned int base, type *res)	\
224 {									\
225 	/* sign, base 2 representation, newline, terminator */		\
226 	char buf[1 + sizeof(type) * 8 + 1 + 1];				\
227 									\
228 	count = min(count, sizeof(buf) - 1);				\
229 	if (copy_from_user(buf, s, count))				\
230 		return -EFAULT;						\
231 	buf[count] = '\0';						\
232 	return g(buf, base, res);					\
233 }									\
234 EXPORT_SYMBOL(f)
235 
236 kstrto_from_user(kstrtoull_from_user,	kstrtoull,	unsigned long long);
237 kstrto_from_user(kstrtoll_from_user,	kstrtoll,	long long);
238 kstrto_from_user(kstrtoul_from_user,	kstrtoul,	unsigned long);
239 kstrto_from_user(kstrtol_from_user,	kstrtol,	long);
240 kstrto_from_user(kstrtouint_from_user,	kstrtouint,	unsigned int);
241 kstrto_from_user(kstrtoint_from_user,	kstrtoint,	int);
242 kstrto_from_user(kstrtou16_from_user,	kstrtou16,	u16);
243 kstrto_from_user(kstrtos16_from_user,	kstrtos16,	s16);
244 kstrto_from_user(kstrtou8_from_user,	kstrtou8,	u8);
245 kstrto_from_user(kstrtos8_from_user,	kstrtos8,	s8);
246