1/* @title: Command Line */
2#pragma once
3#include <compiler.h>
4#include <errno.h>
5#include <linker/symbols.h>
6#include <math/bit.h>
7#include <math/fixed.h>
8#include <math/range.h>
9#include <stdbool.h>
10#include <stddef.h>
11#include <types/type_enum.h>
12
13struct cmdline_entry;
14struct cpu_mask;
15
16enum cmdline_entry_status {
17 CMDLINE_ENTRY_NOT_FOUND = 0,
18 CMDLINE_ENTRY_DEFAULTED,
19 CMDLINE_ENTRY_FOUND,
20};
21
22enum cmdline_entry_flags {
23 CMDLINE_ENTRY_FLAGS_NONE = 0,
24 CMDLINE_ENTRY_SYMBOLIC = 1, /* for namespace parenting purposes */
25 CMDLINE_ENTRY_REQUIRED = 1 << 1,
26 CMDLINE_ENTRY_HIDDEN = 1 << 2, /* exclude from help message */
27};
28
29enum cmdline_value_mode {
30 CMDLINE_MODE_POLYMORPHIC = 0,
31 CMDLINE_MODE_VAR,
32 CMDLINE_MODE_TYPED = CMDLINE_MODE_VAR,
33 CMDLINE_MODE_CUSTOM = CMDLINE_MODE_VAR,
34};
35
36#define CMDLINE_TYPE_OFFSET 6
37
38enum cmdline_type {
39 CMDLINE_TYPE_FIRST_ = CMDLINE_TYPE_OFFSET - 1,
40 CMDLINE_TYPE_BOOL = CMDLINE_TYPE_OFFSET,
41 CMDLINE_TYPE_INT,
42 CMDLINE_TYPE_UINT,
43 CMDLINE_TYPE_FX,
44 CMDLINE_TYPE_DURATION,
45 CMDLINE_TYPE_DATA_SIZE,
46 CMDLINE_TYPE_RANGE,
47 CMDLINE_TYPE_CPU_MASK,
48 CMDLINE_TYPE_MAC,
49 CMDLINE_TYPE_STRING,
50 CMDLINE_TYPE_LIST,
51 CMDLINE_TYPE_ERR,
52 CMDLINE_TYPE_NONE,
53};
54
55#define CMDLINE_TYPES(...) \
56 _DISPATCH(CMDLINE_IMPL_TYPE_BIT, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
57
58struct cmdline_range {
59 uint64_t start;
60 uint64_t end;
61};
62
63struct cmdline_list {
64 size_t count;
65 struct cmdline_value *items;
66};
67
68struct cmdline_value {
69 enum cmdline_value_mode mode;
70
71 union {
72 enum type_enum c_type;
73 enum cmdline_type type;
74 };
75
76 union {
77 void *write_to;
78 void *data;
79 uint64_t u64;
80 int64_t i64;
81 bool b;
82 fx32_32_t fx;
83 time_ns_t duration;
84 };
85
86 /* this is used when mode == CMDLINE_MODE_TYPED or CMDLINE_MODE_CUSTOM */
87 enum errno (*parse)(void *write_to, const char *text);
88};
89
90/* The idea with parent-child relationships:
91 *
92 * The dot is used as a namespace marker. With a given cmdline_entry, we trace
93 * its lineage to build out what name it *really* has.
94 *
95 * For instance, if we have
96 *
97 * apple->parent = orange
98 * orange->parent = pineapple
99 * pineapple->parent = NULL
100 *
101 * the "functional name" of apple is
102 *
103 * pineapple.orange.apple */
104struct __attribute__((aligned(8))) cmdline_entry {
105 const char *name;
106 const char *desc; /* human readable description */
107 const char *arg; /* value format hint e.g. "<hex bytes>", "<device>" */
108 const char *default_val;
109
110 enum cmdline_entry_flags flags;
111 enum cmdline_entry_status status;
112
113 struct cmdline_entry *parent;
114
115 uint64_t types;
116
117 struct range range;
118
119 /* Regarding the variable to write to */
120 struct cmdline_value value;
121
122 const char *const *choices;
123 const struct cmdline_map *mappings;
124 const struct cmdline_flag *flags_table;
125};
126
127struct cmdline_map {
128 const char *name;
129 uint64_t value;
130};
131
132struct cmdline_flag {
133 const char *name;
134 uint64_t value;
135};
136
137#define CMDLINE_MAP(str, enum_val) \
138 {.name = (str), .value = (uint64_t) (enum_val)}
139#define CMDLINE_MAPPINGS(...) \
140 ((const struct cmdline_map[]) {__VA_ARGS__, {NULL, 0}})
141
142#define CMDLINE_FLAG(str, bit_val) \
143 {.name = (str), .value = (uint64_t) (bit_val)}
144#define CMDLINE_FLAGS(...) \
145 ((const struct cmdline_flag[]) {__VA_ARGS__, {NULL, 0}})
146
147#define CMDLINE_CHOICES(...) ((const char *const[]) {__VA_ARGS__, NULL})
148#define CMDLINE_PARSER(fn) .value.parse = (fn)
149
150#define CMDLINE_DECLARE(n, ...) \
151 LINKER_SECTION_OBJECT(struct cmdline_entry, cmdline_entries) \
152 __cmdline_##n = {.name = #n, \
153 .status = CMDLINE_ENTRY_NOT_FOUND, \
154 .types = 0, \
155 .range = RANGE(1, 0), \
156 .choices = NULL, \
157 .mappings = NULL, \
158 .flags_table = NULL, \
159 .value.mode = CMDLINE_MODE_POLYMORPHIC, \
160 .value.type = CMDLINE_TYPE_NONE, \
161 .flags = CMDLINE_ENTRY_FLAGS_NONE, \
162 __VA_ARGS__}
163
164#define CMDLINE_DECLARE_VAR(n, var, ...) \
165 LINKER_SECTION_OBJECT(struct cmdline_entry, cmdline_entries) \
166 __cmdline_##n = {.name = #n, \
167 .status = CMDLINE_ENTRY_NOT_FOUND, \
168 .types = 0, \
169 .range = RANGE(1, 0), \
170 .choices = NULL, \
171 .mappings = NULL, \
172 .flags_table = NULL, \
173 .value.mode = CMDLINE_MODE_VAR, \
174 .value.write_to = &(var), \
175 .value.c_type = TYPE_TO_ENUM((var)), \
176 .value.parse = NULL, \
177 .flags = CMDLINE_ENTRY_FLAGS_NONE, \
178 __VA_ARGS__}
179
180#define CMDLINE_CHILD_DECLARE(parent_n, n, ...) \
181 CMDLINE_DECLARE(parent_n##_##n, .name = #n, .parent = CMDLINE(parent_n), \
182 __VA_ARGS__)
183
184#define CMDLINE_CHILD_DECLARE_VAR(parent_n, n, var, ...) \
185 CMDLINE_DECLARE_VAR(parent_n##_##n, var, .name = #n, \
186 .parent = CMDLINE(parent_n), __VA_ARGS__)
187
188#define CMDLINE_INNER(n, ...) (_CMDLINE_CHILD_KIND_POLY, n, 0, ##__VA_ARGS__)
189
190#define CMDLINE_INNER_VAR(n, var, ...) \
191 (_CMDLINE_CHILD_KIND_VAR, n, var, ##__VA_ARGS__)
192
193#define CMDLINE_CHILDREN_DECLARE(parent_n, ...) \
194 _DISPATCH(_CMDLINE_CHILDREN_MAP, PP_NARG(__VA_ARGS__))(parent_n, \
195 __VA_ARGS__)
196
197#define CMDLINE_EXTRACT(val, var) \
198 _Generic(&(var), \
199 bool *: cmdline_extract_bool((val), (bool *) &(var)), \
200 uint64_t *: cmdline_extract_u64((val), (uint64_t *) &(var)), \
201 int64_t *: cmdline_extract_i64((val), (int64_t *) &(var)), \
202 uint32_t *: cmdline_extract_u32((val), (uint32_t *) &(var)), \
203 int32_t *: cmdline_extract_i32((val), (int32_t *) &(var)), \
204 uint16_t *: cmdline_extract_u16((val), (uint16_t *) &(var)), \
205 int16_t *: cmdline_extract_i16((val), (int16_t *) &(var)), \
206 uint8_t *: cmdline_extract_u8((val), (uint8_t *) &(var)), \
207 int8_t *: cmdline_extract_i8((val), (int8_t *) &(var)), \
208 struct cmdline_range *: cmdline_extract_range( \
209 (val), (struct cmdline_range *) &(var)), \
210 char **: cmdline_extract_string((val), (char **) &(var)), \
211 struct cpu_mask *: cmdline_extract_cpu_mask( \
212 (val), (struct cpu_mask *) &(var)), \
213 struct cmdline_list *: cmdline_extract_list( \
214 (val), (struct cmdline_list *) &(var)), \
215 const char **: cmdline_extract_const_string((val), \
216 (const char **) &(var)))
217
218#define CMDLINE_EXTRACT_LIST(list, type, out_buf, max_count, parse_fn) \
219 ({ \
220 size_t __n = 0; \
221 const struct cmdline_list *__l = (list); \
222 const size_t __max = (max_count); \
223 for (size_t __i = 0; __i < __l->count && __n < __max; __i++) { \
224 type __v = (type) {0}; \
225 if ((parse_fn) (&__l->items[__i], &__v) == ERR_OK) \
226 (out_buf)[__n++] = __v; \
227 } \
228 __n; \
229 })
230
231#define CMDLINE_NODE_1(a) a
232#define CMDLINE_NODE_2(a, b) a##_##b
233#define CMDLINE_NODE_3(a, b, c) a##_##b##_##c
234#define CMDLINE_NODE_4(a, b, c, d) a##_##b##_##c##_##d
235#define CMDLINE_NODE(...) \
236 _DISPATCH(CMDLINE_NODE, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
237
238#define CMDLINE_DEFINE(n) extern struct cmdline_entry CONCAT(__cmdline_, n)
239#define CMDLINE(n) (&CONCAT(__cmdline_, n))
240#define CMDLINE_VALUE(n) cmdline_entry_value_u64(CMDLINE(n))
241
242#define CMDLINE_CHILD_DEFINE(...) CMDLINE_DEFINE(CMDLINE_NODE(__VA_ARGS__))
243#define CMDLINE_CHILD(...) CMDLINE(CMDLINE_NODE(__VA_ARGS__))
244#define CMDLINE_CHILD_VALUE(...) \
245 cmdline_entry_value_u64(CMDLINE_CHILD(__VA_ARGS__))
246
247#define CMDLINE_ENTRY_NAME_LEN_MAX 256
248
249#define cmdline_list_for_each(val, list) \
250 for (size_t __i = 0, __count = (list)->count; \
251 __i < __count && (((val) = (list)->items[__i]), true); __i++)
252
253/* Schema-Driven Subsystem Definitions */
254struct cmdline_schema_prop {
255 const char *name;
256 const char *desc;
257 size_t offset;
258 enum type_enum c_type;
259 uint64_t types;
260 enum errno (*parse)(void *dst, const char *text);
261
262 struct range range;
263
264 const char *const *choices;
265 const struct cmdline_map *mappings;
266 const struct cmdline_flag *flags_table;
267};
268
269typedef void *(*cmdline_instance_resolver_t)(const char *path, size_t path_len);
270
271struct __attribute__((aligned(8))) cmdline_schema {
272 const char *prefix;
273 const char *path_hint;
274 const char *desc;
275 cmdline_instance_resolver_t resolve;
276 const struct cmdline_schema_prop *props;
277 size_t prop_count;
278};
279
280#define CMDLINE_SCHEMA_PROP_PARSER(fn) .parse = (fn)
281
282#define CMDLINE_SCHEMA_PROP(struct_type, member, ...) \
283 {.name = #member, \
284 .offset = offsetof(struct_type, member), \
285 .c_type = TYPE_TO_ENUM(((struct_type *) 0)->member), \
286 .types = 0, \
287 .parse = NULL, \
288 .range = RANGE(1, 0), \
289 .choices = NULL, \
290 .mappings = NULL, \
291 .flags_table = NULL, \
292 __VA_ARGS__}
293
294#define CMDLINE_SCHEMA_DECLARE(n, prefix_str, path_hint_str, desc_str, \
295 resolver_fn, ...) \
296 static const struct cmdline_schema_prop __cmdline_schema_props_##n[] = { \
297 __VA_ARGS__}; \
298 LINKER_SECTION_OBJECT(struct cmdline_schema, cmdline_schemas) \
299 __cmdline_schema_##n = { \
300 .prefix = (prefix_str), \
301 .path_hint = (path_hint_str), \
302 .desc = (desc_str), \
303 .resolve = (resolver_fn), \
304 .props = __cmdline_schema_props_##n, \
305 .prop_count = sizeof(__cmdline_schema_props_##n) / \
306 sizeof(__cmdline_schema_props_##n[0]), \
307 }
308
309#define CMDLINE_GET(key, type, fallback) \
310 ({ \
311 type __out = (fallback); \
312 struct cmdline_entry *__e = cmdline_lookup(key); \
313 if (__e && (__e->status == CMDLINE_ENTRY_FOUND || \
314 __e->status == CMDLINE_ENTRY_DEFAULTED)) { \
315 CMDLINE_EXTRACT(&__e->value, __out); \
316 } \
317 __out; \
318 })
319
320#define cmdline_read_or(key, target_var, fallback) \
321 ({ \
322 bool __overridden = false; \
323 struct cmdline_entry *__e = cmdline_lookup(key); \
324 if (__e && __e->status == CMDLINE_ENTRY_FOUND && \
325 CMDLINE_EXTRACT(&__e->value, target_var) == ERR_OK) { \
326 __overridden = true; \
327 } else { \
328 (target_var) = (fallback); \
329 } \
330 __overridden; \
331 })
332
333void cmdline_parse(const char *input);
334bool cmdline_wants_help(const char *input);
335__noreturn void cmdline_dump_help(void);
336void cmdline_debug_hook(void);
337
338struct cmdline_entry *cmdline_lookup(const char *key);
339uint64_t cmdline_entry_value_u64(const struct cmdline_entry *e);
340
341#include "cmdline_api_internal.h"
342