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: [%ld.%02lu..%ld.%02lu]", lo_whole, lo_frac, hi_whole,
127 hi_frac);
128 } else if (t == CMDLINE_TYPE_DURATION) {
129 if (e->range.hi >= 1000000000ULL && e->range.hi % 1000000000ULL == 0)
130 printf(format: ", range: [%luns..%llus]", e->range.low,
131 (e->range.hi / 1000000000ULL));
132 else if (e->range.hi >= 1000000ULL && e->range.hi % 1000000ULL == 0)
133 printf(format: ", range: [%luns..%llums]", e->range.low,
134 (e->range.hi / 1000000ULL));
135 else if (e->range.hi >= 1000ULL && e->range.hi % 1000ULL == 0)
136 printf(format: ", range: [%luns..%lluus]", e->range.low,
137 (e->range.hi / 1000ULL));
138 else
139 printf(format: ", range: [%luns..%luns]", e->range.low, e->range.hi);
140 } else if (t == CMDLINE_TYPE_INT) {
141 printf(format: ", range: [%ld..%ld]", (int64_t) e->range.low,
142 (int64_t) e->range.hi);
143 } else if (t == CMDLINE_TYPE_UINT || t == CMDLINE_TYPE_DATA_SIZE) {
144 printf(format: ", range: [%llu..%llu]", (unsigned long long) e->range.low,
145 (unsigned long long) e->range.hi);
146 }
147}
148
149static void print_help_metadata(const struct cmdline_entry *e) {
150 if (e->choices) {
151 printf(format: ", choices: [");
152 for (const char *const *c = e->choices; *c != NULL; c++) {
153 printf(format: "%s%s", (c == e->choices) ? "" : ", ", *c);
154 }
155 printf(format: "]");
156 } else if (e->mappings) {
157 printf(format: ", choices: [");
158 for (const struct cmdline_map *m = e->mappings; m->name != NULL; m++) {
159 printf(format: "%s%s", (m == e->mappings) ? "" : ", ", m->name);
160 }
161 printf(format: "]");
162 }
163
164 if (e->flags_table) {
165 printf(format: ", flags: [");
166 for (const struct cmdline_flag *f = e->flags_table; f->name != NULL;
167 f++) {
168 printf(format: "%s%s", (f == e->flags_table) ? "" : "|", f->name);
169 }
170 printf(format: "]");
171 }
172}
173
174cc_noreturn void cmdline_dump_help(void) {
175 printf(format: "charmos kernel command-line options:\n\n");
176 for (struct cmdline_entry *e = __skernel_cmdline_entries;
177 e < __ekernel_cmdline_entries; e++) {
178 if (e->flags & (CMDLINE_ENTRY_HIDDEN | CMDLINE_ENTRY_SYMBOLIC))
179 continue;
180
181 char name[CMDLINE_ENTRY_NAME_LEN_MAX];
182 cmdline_functional_name(ent: e, name_out: name);
183 printf(format: " %s%s%s\n", name, e->arg ? "=" : "", e->arg ? e->arg : "");
184 if (e->desc)
185 printf(format: " %s\n", e->desc);
186 printf(format: " %s",
187 (e->flags & CMDLINE_ENTRY_REQUIRED) ? "required" : "optional");
188 if (e->default_val)
189 printf(format: ", default: %s", e->default_val);
190
191 print_help_range(e);
192 print_help_metadata(e);
193 printf(format: "\n\n");
194 }
195
196 if ((uintptr_t) __skernel_cmdline_schemas <
197 (uintptr_t) __ekernel_cmdline_schemas) {
198 printf(format: "subsystem schemas:\n\n");
199 for (struct cmdline_schema *s = __skernel_cmdline_schemas;
200 s < __ekernel_cmdline_schemas; s++) {
201 const char *hint = s->path_hint ? s->path_hint : "<path>";
202 printf(format: " %s.%s.<property>\n", s->prefix, hint);
203 if (s->desc)
204 printf(format: " %s\n", s->desc);
205 for (size_t i = 0; i < s->prop_count; i++) {
206 const struct cmdline_schema_prop *p = &s->props[i];
207 printf(format: " .%s (%s)", p->name,
208 p->parse ? "custom" : type_enum_str(type: p->c_type));
209 if (p->desc)
210 printf(format: " - %s", p->desc);
211 printf(format: "\n");
212 }
213 printf(format: "\n");
214 }
215 }
216
217 printf(format: "(kernel halted after `help`)\n");
218
219 irq_disable();
220 while (true)
221 cpu_freeze();
222}
223