| 1 | #include "internal.h" |
| 2 | |
| 3 | static 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 | |
| 32 | static 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 | |
| 67 | void 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 | |
| 78 | bool 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 | |
| 115 | static 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 | |
| 155 | static 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 | |