1#include "internal.h"
2
3static enum err cmdline_parse_i64_val(const char *text, int64_t *out) {
4 int64_t v;
5 if (parse_is_int(str: text, out: &v)) {
6 *out = v;
7 return ERR_OK;
8 }
9
10 uint64_t size;
11 if (parse_is_data_size(str: text, out: &size)) {
12 if (size > (uint64_t) INT64_MAX)
13 return ERR_OVERFLOW;
14 *out = (int64_t) size;
15 return ERR_OK;
16 }
17
18 return ERR_INVAL;
19}
20
21static enum err cmdline_parse_u64_val(const char *text, uint64_t *out) {
22 uint64_t v;
23 if (parse_is_uint(str: text, out: &v)) {
24 *out = v;
25 return ERR_OK;
26 }
27
28 uint64_t size;
29 if (parse_is_data_size(str: text, out: &size)) {
30 *out = size;
31 return ERR_OK;
32 }
33
34 return ERR_INVAL;
35}
36
37enum err cmdline_parse_i64(void *write_to, const char *text) {
38 return cmdline_parse_i64_val(text, out: (int64_t *) write_to);
39}
40
41enum err cmdline_parse_u64(void *write_to, const char *text) {
42 return cmdline_parse_u64_val(text, out: (uint64_t *) write_to);
43}
44
45enum err cmdline_parse_bool(void *write_to, const char *text) {
46 bool out = false;
47 if (!parse_is_bool(str: text, out: &out))
48 return ERR_INVAL;
49
50 *(bool *) write_to = out;
51 return ERR_OK;
52}
53
54enum err cmdline_parse_fx(void *write_to, const char *text) {
55 fx32_32_t val = 0;
56 if (!parse_is_fx(str: text, out: &val))
57 return ERR_INVAL;
58 *(fx32_32_t *) write_to = val;
59 return ERR_OK;
60}
61
62enum err cmdline_parse_duration(void *write_to, const char *text) {
63 time_ns_t val = 0;
64 if (!parse_is_duration(str: text, out: &val))
65 return ERR_INVAL;
66 *(time_ns_t *) write_to = val;
67 return ERR_OK;
68}
69
70enum err cmdline_parse_data_size(void *write_to, const char *text) {
71 uint64_t val = 0;
72 if (!parse_is_data_size(str: text, out: &val))
73 return ERR_INVAL;
74 *(uint64_t *) write_to = val;
75 return ERR_OK;
76}
77
78enum err cmdline_parse_cpu_mask(void *write_to, const char *text) {
79 struct cpu_mask mask;
80 if (!parse_is_cpu_mask(str: text, out: &mask, cpu_count: global.core_count))
81 return ERR_INVAL;
82 *(struct cpu_mask *) write_to = mask;
83 return ERR_OK;
84}
85
86enum err cmdline_parse_mac(void *write_to, const char *text) {
87 uint64_t val = 0;
88 if (!parse_is_mac(str: text, out: &val))
89 return ERR_INVAL;
90 *(uint64_t *) write_to = val;
91 return ERR_OK;
92}
93
94enum err cmdline_parse_range(void *write_to, const char *text) {
95 uint64_t start = 0, end = 0;
96 if (!parse_is_range(str: text, start: &start, end: &end))
97 return ERR_INVAL;
98 struct cmdline_range *range = (struct cmdline_range *) write_to;
99 range->start = start;
100 range->end = end;
101 return ERR_OK;
102}
103
104enum err cmdline_parse_string(void *write_to, const char *text) {
105 if (!text)
106 return ERR_INVAL;
107 size_t len = strlen(str: text);
108 char *copy = nofail_valloc(len + 1);
109 memcpy(copy, text, len + 1);
110 *(char **) write_to = copy;
111 return ERR_OK;
112}
113
114static bool detect_bool(const char *text, void *out) {
115 const char *p = text;
116 while (*p == ' ' || *p == '\t')
117 p++;
118
119 if (strcmp(str1: p, str2: "0") == 0 || strcmp(str1: p, str2: "1") == 0)
120 return false;
121
122 return parse_is_bool(str: text, out: (bool *) out);
123}
124
125static bool detect_duration(const char *text, void *out) {
126 const char *p = text;
127 while (*p == ' ' || *p == '\t')
128 p++;
129
130 while (*p >= '0' && *p <= '9')
131 p++;
132
133 while (*p == ' ' || *p == '\t')
134 p++;
135
136 if (*p == '\0')
137 return false;
138
139 return parse_is_duration(str: text, out: (time_ns_t *) out);
140}
141
142static bool detect_data_size(const char *text, void *out) {
143 const char *p = text;
144 while (*p == ' ' || *p == '\t')
145 p++;
146
147 while (*p >= '0' && *p <= '9')
148 p++;
149
150 while (*p == ' ' || *p == '\t')
151 p++;
152
153 if (*p == '\0')
154 return false;
155
156 return parse_is_data_size(str: text, out: (uint64_t *) out);
157}
158
159static bool detect_cpu_mask(const char *text, void *out) {
160 return parse_is_cpu_mask(str: text, out: (struct cpu_mask *) out, cpu_count: global.core_count);
161}
162
163static bool detect_range(const char *text, void *out) {
164 if (!out)
165 return parse_is_range(str: text, NULL, NULL);
166
167 struct cmdline_range *r = (struct cmdline_range *) out;
168 return parse_is_range(str: text, start: &r->start, end: &r->end);
169}
170
171static bool detect_mac(const char *text, void *out) {
172 return parse_is_mac(str: text, out: (uint64_t *) out);
173}
174
175static bool detect_fx(const char *text, void *out) {
176 if (strchr(s: text, c: '.') == NULL)
177 return false;
178
179 return parse_is_fx(str: text, out: (fx32_32_t *) out);
180}
181
182static bool detect_uint(const char *text, void *out) {
183 return parse_is_uint(str: text, out: (uint64_t *) out);
184}
185
186static bool detect_int(const char *text, void *out) {
187 return parse_is_int(str: text, out: (int64_t *) out);
188}
189
190static bool detect_string(const char *text, void *out) {
191 cc_ignore(out);
192 return text != NULL;
193}
194
195const struct cmdline_type_parser cmdline_parsers[] = {
196 {
197 .type = CMDLINE_TYPE_BOOL,
198 .name = "bool",
199 .arg_hint = "<on/off>",
200 .value_size = sizeof(bool),
201 .uses_allocated_ptr = false,
202 .detect = detect_bool,
203 .parse = cmdline_parse_bool,
204 },
205 {
206 .type = CMDLINE_TYPE_DURATION,
207 .name = "duration",
208 .arg_hint = "<time>",
209 .value_size = sizeof(time_ns_t),
210 .uses_allocated_ptr = false,
211 .detect = detect_duration,
212 .parse = cmdline_parse_duration,
213 },
214 {
215 .type = CMDLINE_TYPE_DATA_SIZE,
216 .name = "data_size",
217 .arg_hint = "<size>",
218 .value_size = sizeof(uint64_t),
219 .uses_allocated_ptr = false,
220 .detect = detect_data_size,
221 .parse = cmdline_parse_data_size,
222 },
223 /* must come before cpu_mask: "10" is a CPU mask only
224 * when there are more than 10 CPUs, and untyped
225 * data shouldn't change its type with a CPU count */
226 {
227 .type = CMDLINE_TYPE_UINT,
228 .name = "uint",
229 .arg_hint = "<unsigned-int>",
230 .value_size = sizeof(uint64_t),
231 .uses_allocated_ptr = false,
232 .detect = detect_uint,
233 .parse = cmdline_parse_u64,
234 },
235 {
236 .type = CMDLINE_TYPE_CPU_MASK,
237 .name = "cpu_mask",
238 .arg_hint = "<cpus>",
239 .value_size = sizeof(struct cpu_mask),
240 .uses_allocated_ptr = true,
241 .detect = detect_cpu_mask,
242 .parse = cmdline_parse_cpu_mask,
243 },
244 {
245 .type = CMDLINE_TYPE_RANGE,
246 .name = "range",
247 .arg_hint = "<start-end>",
248 .value_size = sizeof(struct cmdline_range),
249 .uses_allocated_ptr = true,
250 .detect = detect_range,
251 .parse = cmdline_parse_range,
252 },
253 {
254 .type = CMDLINE_TYPE_MAC,
255 .name = "mac",
256 .arg_hint = "<mac-addr>",
257 .value_size = sizeof(uint64_t),
258 .uses_allocated_ptr = false,
259 .detect = detect_mac,
260 .parse = cmdline_parse_mac,
261 },
262 {
263 .type = CMDLINE_TYPE_FX,
264 .name = "fixed_point",
265 .arg_hint = "<float>",
266 .value_size = sizeof(fx32_32_t),
267 .uses_allocated_ptr = false,
268 .detect = detect_fx,
269 .parse = cmdline_parse_fx,
270 },
271 {
272 .type = CMDLINE_TYPE_INT,
273 .name = "int",
274 .arg_hint = "<integer>",
275 .value_size = sizeof(int64_t),
276 .uses_allocated_ptr = false,
277 .detect = detect_int,
278 .parse = cmdline_parse_i64,
279 },
280 {
281 .type = CMDLINE_TYPE_STRING,
282 .name = "string",
283 .arg_hint = "<string>",
284 .value_size = sizeof(char *),
285 .uses_allocated_ptr = true,
286 .detect = detect_string,
287 .parse = cmdline_parse_string,
288 },
289};
290
291const size_t cmdline_parsers_count =
292 sizeof(cmdline_parsers) / sizeof(cmdline_parsers[0]);
293
294#define CMDLINE_AUTO_ARG_POOL_MAX 16
295#define CMDLINE_AUTO_ARG_LEN 64
296
297struct cmdline_auto_arg_entry {
298 uint64_t mask;
299 char str[CMDLINE_AUTO_ARG_LEN];
300};
301
302static struct cmdline_auto_arg_entry auto_arg_pool[CMDLINE_AUTO_ARG_POOL_MAX];
303static size_t auto_arg_pool_count = 0;
304
305const char *cmdline_intern_composite_arg(uint64_t mask,
306 const enum cmdline_type *types,
307 size_t num_types) {
308 for (size_t i = 0; i < auto_arg_pool_count; i++) {
309 if (auto_arg_pool[i].mask == mask)
310 return auto_arg_pool[i].str;
311 }
312
313 enforce(auto_arg_pool_count < CMDLINE_AUTO_ARG_POOL_MAX, CAPACITY_EXCEEDED);
314 struct cmdline_auto_arg_entry *slot = &auto_arg_pool[auto_arg_pool_count++];
315 slot->mask = mask;
316
317 char temp[CMDLINE_AUTO_ARG_LEN];
318 size_t idx = 0;
319 temp[idx++] = '<';
320
321 for (size_t i = 0; i < num_types; i++) {
322 const char *hint = cmdline_type_raw_hint(type: types[i]);
323 if (!hint)
324 continue;
325 size_t len = strlen(str: hint);
326 if (idx + len + 2 < CMDLINE_AUTO_ARG_LEN) {
327 memcpy(&temp[idx], hint, len);
328 idx += len;
329 if (i + 1 < num_types)
330 temp[idx++] = '|';
331 }
332 }
333
334 temp[idx++] = '>';
335 temp[idx] = '\0';
336
337 memcpy(slot->str, temp, idx + 1);
338 return slot->str;
339}
340
341void cmdline_assign_all_args(void) {
342 static bool assigned = false;
343 if (assigned)
344 return;
345 assigned = true;
346
347 for (struct cmdline_entry *ent = __skernel_cmdline_entries;
348 ent < __ekernel_cmdline_entries; ent++) {
349 if (ent->flags & (CMDLINE_ENTRY_HIDDEN | CMDLINE_ENTRY_SYMBOLIC))
350 continue;
351 if (ent->arg != NULL)
352 continue;
353
354 if (ent->value.mode == CMDLINE_MODE_CUSTOM && ent->value.parse) {
355 for (size_t i = 0; i < cmdline_parsers_count; i++) {
356 if (cmdline_parsers[i].parse == ent->value.parse) {
357 ent->arg = cmdline_parsers[i].arg_hint;
358 break;
359 }
360 }
361 } else if (ent->value.mode == CMDLINE_MODE_TYPED) {
362 enum cmdline_type cmd_t =
363 cmdline_type_enum_to_cmdline_type(t: ent->value.c_type);
364 if (cmd_t != CMDLINE_TYPE_NONE)
365 ent->arg = CMDLINE_EXPR_TYPE_TO_STR(cmd_t);
366 } else if (ent->value.mode == CMDLINE_MODE_POLYMORPHIC) {
367 uint64_t mask = cmdline_entry_get_accepted_mask(e: ent);
368 if (mask == UINT64_MAX || mask == 0)
369 continue;
370
371 size_t count = popcount(n: mask);
372 if (count == 0)
373 continue;
374
375 enum cmdline_type types[CMDLINE_MAX_TYPE_ARGS];
376 size_t num_types = cmdline_extract_type_bits(mask, out: types);
377
378 if (num_types == 1) {
379 ent->arg = CMDLINE_EXPR_TYPE_TO_STR(types[0]);
380 } else if (num_types > 1) {
381 ent->arg = cmdline_intern_composite_arg(mask, types, num_types);
382 }
383 }
384 }
385}
386