1#include "internal.h"
2
3static enum errno 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 errno 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 errno cmdline_parse_i64(void *write_to, const char *text) {
38 return cmdline_parse_i64_val(text, out: (int64_t *) write_to);
39}
40
41enum errno cmdline_parse_u64(void *write_to, const char *text) {
42 return cmdline_parse_u64_val(text, out: (uint64_t *) write_to);
43}
44
45enum errno 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 errno 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 errno 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 errno 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 errno 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, n_cpus: global.core_count))
81 return ERR_INVAL;
82 *(struct cpu_mask *) write_to = mask;
83 return ERR_OK;
84}
85
86enum errno 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 errno 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 errno 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 = kmalloc_or_die(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, n_cpus: 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 (void) 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 {
224 .type = CMDLINE_TYPE_CPU_MASK,
225 .name = "cpu_mask",
226 .arg_hint = "<cpus>",
227 .value_size = sizeof(struct cpu_mask),
228 .uses_allocated_ptr = true,
229 .detect = detect_cpu_mask,
230 .parse = cmdline_parse_cpu_mask,
231 },
232 {
233 .type = CMDLINE_TYPE_RANGE,
234 .name = "range",
235 .arg_hint = "<start-end>",
236 .value_size = sizeof(struct cmdline_range),
237 .uses_allocated_ptr = true,
238 .detect = detect_range,
239 .parse = cmdline_parse_range,
240 },
241 {
242 .type = CMDLINE_TYPE_MAC,
243 .name = "mac",
244 .arg_hint = "<mac-addr>",
245 .value_size = sizeof(uint64_t),
246 .uses_allocated_ptr = false,
247 .detect = detect_mac,
248 .parse = cmdline_parse_mac,
249 },
250 {
251 .type = CMDLINE_TYPE_FX,
252 .name = "fixed_point",
253 .arg_hint = "<float>",
254 .value_size = sizeof(fx32_32_t),
255 .uses_allocated_ptr = false,
256 .detect = detect_fx,
257 .parse = cmdline_parse_fx,
258 },
259 {
260 .type = CMDLINE_TYPE_UINT,
261 .name = "uint",
262 .arg_hint = "<unsigned-int>",
263 .value_size = sizeof(uint64_t),
264 .uses_allocated_ptr = false,
265 .detect = detect_uint,
266 .parse = cmdline_parse_u64,
267 },
268 {
269 .type = CMDLINE_TYPE_INT,
270 .name = "int",
271 .arg_hint = "<integer>",
272 .value_size = sizeof(int64_t),
273 .uses_allocated_ptr = false,
274 .detect = detect_int,
275 .parse = cmdline_parse_i64,
276 },
277 {
278 .type = CMDLINE_TYPE_STRING,
279 .name = "string",
280 .arg_hint = "<string>",
281 .value_size = sizeof(char *),
282 .uses_allocated_ptr = true,
283 .detect = detect_string,
284 .parse = cmdline_parse_string,
285 },
286};
287
288const size_t cmdline_parsers_count =
289 sizeof(cmdline_parsers) / sizeof(cmdline_parsers[0]);
290
291#define CMDLINE_AUTO_ARG_POOL_MAX 16
292#define CMDLINE_AUTO_ARG_LEN 64
293
294struct cmdline_auto_arg_entry {
295 uint64_t mask;
296 char str[CMDLINE_AUTO_ARG_LEN];
297};
298
299static struct cmdline_auto_arg_entry auto_arg_pool[CMDLINE_AUTO_ARG_POOL_MAX];
300static size_t auto_arg_pool_count = 0;
301
302const char *cmdline_intern_composite_arg(uint64_t mask,
303 const enum cmdline_type *types,
304 size_t num_types) {
305 for (size_t i = 0; i < auto_arg_pool_count; i++) {
306 if (auto_arg_pool[i].mask == mask)
307 return auto_arg_pool[i].str;
308 }
309
310 kassert(auto_arg_pool_count < CMDLINE_AUTO_ARG_POOL_MAX);
311 struct cmdline_auto_arg_entry *slot = &auto_arg_pool[auto_arg_pool_count++];
312 slot->mask = mask;
313
314 char temp[CMDLINE_AUTO_ARG_LEN];
315 size_t idx = 0;
316 temp[idx++] = '<';
317
318 for (size_t i = 0; i < num_types; i++) {
319 const char *hint = cmdline_type_raw_hint(type: types[i]);
320 if (!hint)
321 continue;
322 size_t len = strlen(str: hint);
323 if (idx + len + 2 < CMDLINE_AUTO_ARG_LEN) {
324 memcpy(&temp[idx], hint, len);
325 idx += len;
326 if (i + 1 < num_types)
327 temp[idx++] = '|';
328 }
329 }
330
331 temp[idx++] = '>';
332 temp[idx] = '\0';
333
334 memcpy(slot->str, temp, idx + 1);
335 return slot->str;
336}
337
338void cmdline_assign_all_args(void) {
339 static bool assigned = false;
340 if (assigned)
341 return;
342 assigned = true;
343
344 for (struct cmdline_entry *ent = __skernel_cmdline_entries;
345 ent < __ekernel_cmdline_entries; ent++) {
346 if (ent->flags & (CMDLINE_ENTRY_HIDDEN | CMDLINE_ENTRY_SYMBOLIC))
347 continue;
348 if (ent->arg != NULL)
349 continue;
350
351 if (ent->value.mode == CMDLINE_MODE_TYPED) {
352 enum cmdline_type cmd_t =
353 cmdline_type_enum_to_cmdline_type(t: ent->value.c_type);
354 if (cmd_t != CMDLINE_TYPE_NONE)
355 ent->arg = CMDLINE_EXPR_TYPE_TO_STR(cmd_t);
356 } else if (ent->value.mode == CMDLINE_MODE_CUSTOM) {
357 for (size_t i = 0; i < cmdline_parsers_count; i++) {
358 if (cmdline_parsers[i].parse == ent->value.parse) {
359 ent->arg = cmdline_parsers[i].arg_hint;
360 break;
361 }
362 }
363 } else if (ent->value.mode == CMDLINE_MODE_POLYMORPHIC) {
364 uint64_t mask = cmdline_entry_get_accepted_mask(e: ent);
365 if (mask == UINT64_MAX || mask == 0)
366 continue;
367
368 size_t count = popcount(n: mask);
369 if (count == 0)
370 continue;
371
372 enum cmdline_type types[CMDLINE_MAX_TYPE_ARGS];
373 size_t num_types = cmdline_extract_type_bits(mask, out: types);
374
375 if (num_types == 1) {
376 ent->arg = CMDLINE_EXPR_TYPE_TO_STR(types[0]);
377 } else if (num_types > 1) {
378 ent->arg = cmdline_intern_composite_arg(mask, types, num_types);
379 }
380 }
381 }
382}
383