1#include "internal.h"
2
3static void validate_default_choice_or_mapping(const struct cmdline_entry *e,
4 const char *name) {
5 if (e->choices && e->default_val) {
6 if (!cmdline_has_choice(choices: e->choices, val: e->default_val))
7 panic("cmdline entry '%s' default value '%s' is not in "
8 "choices list",
9 name, e->default_val);
10 }
11
12 if (e->mappings && e->default_val) {
13 uint64_t mapped_val = 0;
14 if (!cmdline_lookup_mapping(mappings: e->mappings, val: e->default_val, out: &mapped_val))
15 panic("cmdline entry '%s' default value '%s' is not in "
16 "mappings list",
17 name, e->default_val);
18 }
19
20 if (e->flags_table && e->default_val) {
21 uint64_t mask = 0;
22 const char *err_tok = NULL;
23 size_t err_len = 0;
24 if (!cmdline_parse_flags(table: e->flags_table, val: e->default_val, out_mask: &mask,
25 err_token: &err_tok, err_len: &err_len))
26 panic("cmdline entry '%s' default value '%s' contains invalid "
27 "flag '%.*s'",
28 name, e->default_val, (int) err_len, err_tok);
29 }
30}
31
32static void validate_default_range(const struct cmdline_entry *e,
33 const char *name) {
34 if (!cmdline_entry_has_range(e) || !e->default_val)
35 return;
36
37 enum cmdline_type t = cmdline_entry_effective_type(e);
38
39 if (t == CMDLINE_TYPE_FX) {
40 fx32_32_t val = 0;
41 if (!parse_is_fx(str: e->default_val, out: &val) ||
42 val < (fx32_32_t) e->range.low || val > (fx32_32_t) e->range.hi)
43 panic("cmdline entry '%s' default value '%s' out of range", name,
44 e->default_val);
45 } else if (t == CMDLINE_TYPE_DURATION) {
46 time_ns_t val = 0;
47 if (!parse_is_duration(str: e->default_val, out: &val) ||
48 !RANGE_CONTAINS(e->range, val))
49 panic("cmdline entry '%s' default value '%s' out of range", name,
50 e->default_val);
51 } else if (t == CMDLINE_TYPE_INT) {
52 int64_t val = 0;
53 if (!parse_is_int(str: e->default_val, out: &val) ||
54 val < (int64_t) e->range.low || val > (int64_t) e->range.hi)
55 panic("cmdline entry '%s' default value '%s' out of range", name,
56 e->default_val);
57 } else if (t == CMDLINE_TYPE_UINT || t == CMDLINE_TYPE_DATA_SIZE) {
58 uint64_t val = 0;
59 if ((!parse_is_uint(str: e->default_val, out: &val) &&
60 !parse_is_data_size(str: e->default_val, out: &val)) ||
61 !RANGE_CONTAINS(e->range, val))
62 panic("cmdline entry '%s' default value '%s' out of range", name,
63 e->default_val);
64 }
65}
66
67void cmdline_validate_defaults(void) {
68 for (struct cmdline_entry *e = __skernel_cmdline_entries;
69 e < __ekernel_cmdline_entries; e++) {
70 char name[CMDLINE_ENTRY_NAME_LEN_MAX];
71 cmdline_functional_name(ent: e, name_out: name);
72
73 validate_default_choice_or_mapping(e, name);
74 validate_default_range(e, name);
75 }
76}
77
78bool cmdline_wants_help(const char *input) {
79 cmdline_assign_all_args();
80
81 const char *p = input;
82 while (*p) {
83 while (*p == ' ' || *p == '\t')
84 p++;
85
86 if (*p == '\0')
87 break;
88
89 if (*p == '"') {
90 p++;
91 while (*p && *p != '"') {
92 if (*p == '\\' && *(p + 1) != '\0')
93 p += 2;
94 else
95 p++;
96 }
97 if (*p == '"')
98 p++;
99 continue;
100 }
101
102 const char *word_start = p;
103 while (*p && *p != ' ' && *p != '\t')
104 p++;
105
106 size_t len = p - word_start;
107 if ((len == 4 && strncmp(s1: word_start, s2: "help", n: 4) == 0) ||
108 (len == 6 && strncmp(s1: word_start, s2: "--help", n: 6) == 0) ||
109 (len == 2 && strncmp(s1: word_start, s2: "-h", n: 2) == 0))
110 return true;
111 }
112 return false;
113}
114
115static void print_help_range(const struct cmdline_entry *e) {
116 if (!cmdline_entry_has_range(e))
117 return;
118
119 enum cmdline_type t = cmdline_entry_effective_type(e);
120
121 if (t == CMDLINE_TYPE_FX) {
122 int64_t lo_whole = ((int64_t) e->range.low) >> 32;
123 uint64_t lo_frac = ((e->range.low & 0xFFFFFFFFULL) * 100) >> 32;
124 int64_t hi_whole = ((int64_t) e->range.hi) >> 32;
125 uint64_t hi_frac = ((e->range.hi & 0xFFFFFFFFULL) * 100) >> 32;
126 printf(format: ", range: [%lld.%02llu..%lld.%02llu]", (long long) lo_whole,
127 (unsigned long long) lo_frac, (long long) hi_whole,
128 (unsigned long long) hi_frac);
129 } else if (t == CMDLINE_TYPE_DURATION) {
130 if (e->range.hi >= 1000000000ULL && e->range.hi % 1000000000ULL == 0)
131 printf(format: ", range: [%lluns..%llus]",
132 (unsigned long long) e->range.low,
133 (unsigned long long) (e->range.hi / 1000000000ULL));
134 else if (e->range.hi >= 1000000ULL && e->range.hi % 1000000ULL == 0)
135 printf(format: ", range: [%lluns..%llums]",
136 (unsigned long long) e->range.low,
137 (unsigned long long) (e->range.hi / 1000000ULL));
138 else if (e->range.hi >= 1000ULL && e->range.hi % 1000ULL == 0)
139 printf(format: ", range: [%lluns..%lluus]",
140 (unsigned long long) e->range.low,
141 (unsigned long long) (e->range.hi / 1000ULL));
142 else
143 printf(format: ", range: [%lluns..%lluns]",
144 (unsigned long long) e->range.low,
145 (unsigned long long) e->range.hi);
146 } else if (t == CMDLINE_TYPE_INT) {
147 printf(format: ", range: [%lld..%lld]", (long long) (int64_t) e->range.low,
148 (long long) (int64_t) e->range.hi);
149 } else if (t == CMDLINE_TYPE_UINT || t == CMDLINE_TYPE_DATA_SIZE) {
150 printf(format: ", range: [%llu..%llu]", (unsigned long long) e->range.low,
151 (unsigned long long) e->range.hi);
152 }
153}
154
155static void print_help_metadata(const struct cmdline_entry *e) {
156 if (e->choices) {
157 printf(format: ", choices: [");
158 for (const char *const *c = e->choices; *c != NULL; c++) {
159 printf(format: "%s%s", (c == e->choices) ? "" : ", ", *c);
160 }
161 printf(format: "]");
162 } else if (e->mappings) {
163 printf(format: ", choices: [");
164 for (const struct cmdline_map *m = e->mappings; m->name != NULL; m++) {
165 printf(format: "%s%s", (m == e->mappings) ? "" : ", ", m->name);
166 }
167 printf(format: "]");
168 }
169
170 if (e->flags_table) {
171 printf(format: ", flags: [");
172 for (const struct cmdline_flag *f = e->flags_table; f->name != NULL;
173 f++) {
174 printf(format: "%s%s", (f == e->flags_table) ? "" : "|", f->name);
175 }
176 printf(format: "]");
177 }
178}
179
180__noreturn void cmdline_dump_help(void) {
181 printf(format: "charmos kernel command-line options:\n\n");
182 for (struct cmdline_entry *e = __skernel_cmdline_entries;
183 e < __ekernel_cmdline_entries; e++) {
184 if (e->flags & (CMDLINE_ENTRY_HIDDEN | CMDLINE_ENTRY_SYMBOLIC))
185 continue;
186
187 char name[CMDLINE_ENTRY_NAME_LEN_MAX];
188 cmdline_functional_name(ent: e, name_out: name);
189 printf(format: " %s%s%s\n", name, e->arg ? "=" : "", e->arg ? e->arg : "");
190 if (e->desc)
191 printf(format: " %s\n", e->desc);
192 printf(format: " %s",
193 (e->flags & CMDLINE_ENTRY_REQUIRED) ? "required" : "optional");
194 if (e->default_val)
195 printf(format: ", default: %s", e->default_val);
196
197 print_help_range(e);
198 print_help_metadata(e);
199 printf(format: "\n\n");
200 }
201
202 if (__skernel_cmdline_schemas < __ekernel_cmdline_schemas) {
203 printf(format: "subsystem schemas:\n\n");
204 for (struct cmdline_schema *s = __skernel_cmdline_schemas;
205 s < __ekernel_cmdline_schemas; s++) {
206 const char *hint = s->path_hint ? s->path_hint : "<path>";
207 printf(format: " %s.%s.<property>\n", s->prefix, hint);
208 if (s->desc)
209 printf(format: " %s\n", s->desc);
210 for (size_t i = 0; i < s->prop_count; i++) {
211 const struct cmdline_schema_prop *p = &s->props[i];
212 printf(format: " .%s (%s)", p->name,
213 p->parse ? "custom" : type_enum_str(type: p->c_type));
214 if (p->desc)
215 printf(format: " - %s", p->desc);
216 printf(format: "\n");
217 }
218 printf(format: "\n");
219 }
220 }
221
222 printf(format: "(kernel halted after `help`)\n");
223
224 disable_interrupts();
225 for (;;)
226 hcf();
227}
228