blob: f5228d69e1b97debea1ba90f3be3e5a085104edd [file] [log] [blame]
Sergii Dmytruk04bd9652023-11-17 19:31:20 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <unistd.h>
4
5#include <errno.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10
11#include <commonlib/bsd/helpers.h>
12
13#include "data.h"
14#include "guids.h"
15#include "storage.h"
16#include "udk2017.h"
17#include "vs.h"
18
19struct subcommand_t {
20 const char *name;
21 const char *description;
22 void (*print_help)(FILE *f, const struct subcommand_t *info);
23 int (*process)(int argc, char *argv[], const char store_file[]);
24};
25
26static void help_get(FILE *f, const struct subcommand_t *info);
27static void help_guids(FILE *f, const struct subcommand_t *info);
28static void help_help(FILE *f, const struct subcommand_t *info);
29static void help_list(FILE *f, const struct subcommand_t *info);
30static void help_remove(FILE *f, const struct subcommand_t *info);
31static void help_set(FILE *f, const struct subcommand_t *info);
32static int process_get(int argc, char *argv[], const char store_file[]);
33static int process_guids(int argc, char *argv[], const char store_file[]);
34static int process_help(int argc, char *argv[], const char store_file[]);
35static int process_list(int argc, char *argv[], const char store_file[]);
36static int process_remove(int argc, char *argv[], const char store_file[]);
37static int process_set(int argc, char *argv[], const char store_file[]);
38
39static const struct subcommand_t sub_commands[] = {
40 {
41 .name = "get",
42 .description = "display current value of a variable",
43 .print_help = &help_get,
44 .process = &process_get,
45 },
46 {
47 .name = "guids",
48 .description = "show GUID to alias mapping",
49 .print_help = &help_guids,
50 .process = &process_guids,
51 },
52 {
53 .name = "help",
54 .description = "provide built-in help",
55 .print_help = &help_help,
56 .process = &process_help,
57 },
58 {
59 .name = "list",
60 .description = "list variables present in the store",
61 .print_help = &help_list,
62 .process = &process_list,
63 },
64 {
65 .name = "remove",
66 .description = "remove a variable from the store",
67 .print_help = &help_remove,
68 .process = &process_remove,
69 },
70 {
71 .name = "set",
72 .description = "add or updates a variable in the store",
73 .print_help = &help_set,
74 .process = &process_set,
75 },
76};
77
78static const int sub_command_count = ARRAY_SIZE(sub_commands);
79
Sergii Dmytruk89e056b2024-03-02 17:02:00 +020080static const char *USAGE_FMT = "Usage: %s smm-store-file|rom sub-command\n"
Sergii Dmytruk04bd9652023-11-17 19:31:20 +020081 " %s -h|--help\n";
82
83static const char *program_name;
84
85static void print_program_usage(void)
86{
87 fprintf(stderr, USAGE_FMT, program_name, program_name);
88 exit(EXIT_FAILURE);
89}
90
91static void print_sub_command_usage(const char sub_command[])
92{
93 fprintf(stderr, "\n");
94 fprintf(stderr, USAGE_FMT, program_name, program_name);
95 fprintf(stderr, "\n");
96
97 for (int i = 0; i < sub_command_count; ++i) {
98 const struct subcommand_t *cmd = &sub_commands[i];
99 if (!str_eq(cmd->name, sub_command))
100 continue;
101
102 cmd->print_help(stderr, cmd);
103 break;
104 }
105
106 exit(EXIT_FAILURE);
107}
108
109static void print_help(void)
110{
111 printf(USAGE_FMT, program_name, program_name);
112
113 printf("\n");
114 printf("Sub-commands:\n");
115 for (int i = 0; i < sub_command_count; ++i) {
116 const struct subcommand_t *cmd = &sub_commands[i];
117 printf(" * %-6s - %s\n", cmd->name, cmd->description);
118 }
119}
120
121static void print_types(FILE *f)
122{
123 fprintf(f, "Types and their values:\n");
124 fprintf(f, " * bool (true, false)\n");
125 fprintf(f, " * uint8 (0-255)\n");
Sergii Dmytrukdd6c3b42024-05-22 20:31:46 +0300126 fprintf(f, " * uint16 (0-65535)\n");
127 fprintf(f, " * uint32 (0-4294967295)\n");
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200128 fprintf(f, " * ascii (NUL-terminated)\n");
129 fprintf(f, " * unicode (widened and NUL-terminated)\n");
130 fprintf(f, " * raw (output only; raw bytes on output)\n");
131}
132
133static void help_set(FILE *f, const struct subcommand_t *info)
134{
135 fprintf(f, "Create or update a variable:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200136 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200137 fprintf(f, " -g vendor-guid \\\n");
138 fprintf(f, " -n variable-name \\\n");
139 fprintf(f, " -t variable-type \\\n");
140 fprintf(f, " -v value\n");
141 fprintf(f, "\n");
142 print_types(f);
143}
144
145static int process_set(int argc, char *argv[], const char store_file[])
146{
147 const char *name = NULL;
148 const char *value = NULL;
149 const char *type_str = NULL;
150 const char *guid_str = NULL;
151 int opt;
152 while ((opt = getopt(argc, argv, "n:t:v:g:")) != -1) {
153 switch (opt) {
154 case 'n':
155 name = optarg;
156 break;
157 case 't':
158 type_str = optarg;
159 break;
160 case 'v':
161 value = optarg;
162 break;
163 case 'g':
164 guid_str = optarg;
165 break;
166
167 case '?': /* parsing error */
168 print_sub_command_usage(argv[0]);
169 }
170 }
171
172 if (argv[optind] != NULL) {
173 fprintf(stderr, "First unexpected positional argument: %s\n",
174 argv[optind]);
175 print_sub_command_usage(argv[0]);
176 }
177
178 if (name == NULL || value == NULL || type_str == NULL ||
179 guid_str == NULL) {
180 fprintf(stderr, "All options are required\n");
181 print_sub_command_usage(argv[0]);
182 }
183
184 if (name[0] == '\0') {
185 fprintf(stderr, "Variable name can't be empty\n");
186 print_sub_command_usage(argv[0]);
187 }
188
189 EFI_GUID guid;
190 if (!parse_guid(guid_str, &guid)) {
191 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
192 return EXIT_FAILURE;
193 }
194
195 enum data_type type;
196 if (!parse_data_type(type_str, &type)) {
197 fprintf(stderr, "Failed to parse type: %s\n", type_str);
198 return EXIT_FAILURE;
199 }
200
201 size_t data_size;
202 void *data = make_data(value, &data_size, type);
203 if (data == NULL) {
204 fprintf(stderr, "Failed to parse value \"%s\" as %s\n",
205 value, type_str);
206 return EXIT_FAILURE;
207 }
208
209 struct storage_t storage;
210 if (!storage_open(store_file, &storage, /*rw=*/true)) {
211 free(data);
212 return EXIT_FAILURE;
213 }
214
215 struct var_t *var = vs_find(&storage.vs, name, &guid);
216 if (var == NULL) {
217 var = vs_new_var(&storage.vs);
218 var->name = to_uchars(name, &var->name_size);
219 var->data = data;
220 var->data_size = data_size;
221 var->guid = guid;
222 } else {
223 free(var->data);
224 var->data = data;
225 var->data_size = data_size;
226 }
227
228 return storage_write_back(&storage) ? EXIT_SUCCESS : EXIT_FAILURE;
229}
230
231static void help_list(FILE *f, const struct subcommand_t *info)
232{
233 fprintf(f, "List variables in the store:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200234 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200235}
236
237static int process_list(int argc, char *argv[], const char store_file[])
238{
239 if (argc != 1) {
240 fprintf(stderr, "Invalid invocation\n");
241 print_sub_command_usage(argv[0]);
242 }
243
244 struct storage_t storage;
245 if (!storage_open(store_file, &storage, /*rw=*/false))
246 return EXIT_FAILURE;
247
248 for (struct var_t *v = storage.vs.vars; v != NULL; v = v->next) {
249 char *name = to_chars(v->name, v->name_size);
250 char *guid = format_guid(&v->guid, /*use_alias=*/true);
251
252 printf("%-*s:%s (%zu %s)\n", GUID_LEN, guid, name, v->data_size,
253 v->data_size == 1 ? "byte" : "bytes");
254
255 free(name);
256 free(guid);
257 }
258
259 storage_drop(&storage);
260 return EXIT_SUCCESS;
261}
262
263static void help_get(FILE *f, const struct subcommand_t *info)
264{
265 fprintf(f, "Read variable's value:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200266 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200267 fprintf(f, " -g vendor-guid \\\n");
268 fprintf(f, " -n variable-name \\\n");
269 fprintf(f, " -t variable-type\n");
270 fprintf(f, "\n");
271 print_types(f);
272}
273
274static int process_get(int argc, char *argv[], const char store_file[])
275{
276 const char *name = NULL;
277 const char *type_str = NULL;
278 const char *guid_str = NULL;
279 int opt;
280 while ((opt = getopt(argc, argv, "n:g:t:")) != -1) {
281 switch (opt) {
282 case 'n':
283 name = optarg;
284 break;
285 case 'g':
286 guid_str = optarg;
287 break;
288 case 't':
289 type_str = optarg;
290 break;
291
292 case '?': /* parsing error */
293 print_sub_command_usage(argv[0]);
294 }
295 }
296
297 if (name == NULL || type_str == NULL || guid_str == NULL) {
298 fprintf(stderr, "All options are required\n");
299 print_sub_command_usage(argv[0]);
300 }
301
302 EFI_GUID guid;
303 if (!parse_guid(guid_str, &guid)) {
304 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
305 return EXIT_FAILURE;
306 }
307
308 enum data_type type;
309 if (!parse_data_type(type_str, &type)) {
310 fprintf(stderr, "Failed to parse type: %s\n", type_str);
311 return EXIT_FAILURE;
312 }
313
314 struct storage_t storage;
315 if (!storage_open(store_file, &storage, /*rw=*/false))
316 return EXIT_FAILURE;
317
318 int result = EXIT_SUCCESS;
319
320 struct var_t *var = vs_find(&storage.vs, name, &guid);
321 if (var == NULL) {
322 result = EXIT_FAILURE;
323 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
324 guid_str, name);
325 } else if (var->data_size == 0) {
326 fprintf(stderr, "There is no data to show.\n");
327 result = EXIT_FAILURE;
328 } else {
329 print_data(var->data, var->data_size, type);
330 }
331
332 storage_drop(&storage);
333 return result;
334}
335
336static void help_help(FILE *f, const struct subcommand_t *info)
337{
338 fprintf(f, "Display generic help:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200339 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200340 fprintf(f, "\n");
341 fprintf(f, "Display sub-command help:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200342 fprintf(f, " %s smm-store-file|rom %s sub-command\n",
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200343 program_name, info->name);
344}
345
346static int process_help(int argc, char *argv[], const char store_file[])
347{
348 (void)store_file;
349
350 if (argc == 1) {
351 print_help();
352 return EXIT_SUCCESS;
353 }
354
355 if (argc != 2) {
356 fprintf(stderr, "Invalid invocation\n");
357 print_sub_command_usage(argv[0]);
358 return EXIT_FAILURE;
359 }
360
361 const char *sub_command = argv[1];
362
363 for (int i = 0; i < sub_command_count; ++i) {
364 const struct subcommand_t *cmd = &sub_commands[i];
365 if (!str_eq(cmd->name, sub_command))
366 continue;
367
368 cmd->print_help(stdout, cmd);
369 return EXIT_SUCCESS;
370 }
371
372 fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
373 print_help();
374 return EXIT_FAILURE;
375}
376
377static void help_remove(FILE *f, const struct subcommand_t *info)
378{
379 fprintf(f, "Remove a variable:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200380 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200381 fprintf(f, " -g vendor-guid \\\n");
382 fprintf(f, " -n variable-name\n");
383}
384
385static int process_remove(int argc, char *argv[], const char store_file[])
386{
387 const char *name = NULL;
388 const char *guid_str = NULL;
389 int opt;
390 while ((opt = getopt(argc, argv, "n:g:")) != -1) {
391 switch (opt) {
392 case 'n':
393 name = optarg;
394 break;
395 case 'g':
396 guid_str = optarg;
397 break;
398
399 case '?': /* parsing error */
400 print_sub_command_usage(argv[0]);
401 }
402 }
403
404 if (name == NULL || guid_str == NULL) {
405 fprintf(stderr, "All options are required\n");
406 print_sub_command_usage(argv[0]);
407 }
408
409 EFI_GUID guid;
410 if (!parse_guid(guid_str, &guid)) {
411 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
412 return EXIT_FAILURE;
413 }
414
415 struct storage_t storage;
416 if (!storage_open(store_file, &storage, /*rw=*/true))
417 return EXIT_FAILURE;
418
419 int result = EXIT_SUCCESS;
420
421 struct var_t *var = vs_find(&storage.vs, name, &guid);
422 if (var == NULL) {
423 result = EXIT_FAILURE;
424 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
425 guid_str, name);
426 } else {
427 vs_delete(&storage.vs, var);
428 }
429
430 storage_write_back(&storage);
431 return result;
432}
433
434static void help_guids(FILE *f, const struct subcommand_t *info)
435{
436 fprintf(f, "List recognized GUIDS:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200437 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200438}
439
440static int process_guids(int argc, char *argv[], const char store_file[])
441{
442 (void)store_file;
443
444 if (argc != 1) {
445 fprintf(stderr, "Invalid invocation\n");
446 print_sub_command_usage(argv[0]);
447 }
448
449 for (int i = 0; i < known_guid_count; ++i) {
450 char *guid = format_guid(&known_guids[i].guid,
451 /*use_alias=*/false);
452 printf("%-10s -> %s\n", known_guids[i].alias, guid);
453 free(guid);
454 }
455 return EXIT_SUCCESS;
456}
457
458int main(int argc, char *argv[])
459{
460 program_name = argv[0];
461
462 if (argc > 1 && (str_eq(argv[1], "-h") || str_eq(argv[1], "--help"))) {
463 print_help();
464 exit(EXIT_SUCCESS);
465 }
466
467 if (argc < 3)
468 print_program_usage();
469
470 const char *store_file = argv[1];
471 const char *sub_command = argv[2];
472
473 int sub_command_argc = argc - 2;
474 char **sub_command_argv = argv + 2;
475
476 for (int i = 0; i < sub_command_count; ++i) {
477 const struct subcommand_t *cmd = &sub_commands[i];
478 if (!str_eq(cmd->name, sub_command))
479 continue;
480
481 return cmd->process(sub_command_argc,
482 sub_command_argv,
483 store_file);
484 }
485
486 fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
487 print_help();
488 return EXIT_FAILURE;
489}