blob: 4857b624ba64e2461a44f5c16328d746a7e8fb31 [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");
126 fprintf(f, " * ascii (NUL-terminated)\n");
127 fprintf(f, " * unicode (widened and NUL-terminated)\n");
128 fprintf(f, " * raw (output only; raw bytes on output)\n");
129}
130
131static void help_set(FILE *f, const struct subcommand_t *info)
132{
133 fprintf(f, "Create or update a variable:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200134 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200135 fprintf(f, " -g vendor-guid \\\n");
136 fprintf(f, " -n variable-name \\\n");
137 fprintf(f, " -t variable-type \\\n");
138 fprintf(f, " -v value\n");
139 fprintf(f, "\n");
140 print_types(f);
141}
142
143static int process_set(int argc, char *argv[], const char store_file[])
144{
145 const char *name = NULL;
146 const char *value = NULL;
147 const char *type_str = NULL;
148 const char *guid_str = NULL;
149 int opt;
150 while ((opt = getopt(argc, argv, "n:t:v:g:")) != -1) {
151 switch (opt) {
152 case 'n':
153 name = optarg;
154 break;
155 case 't':
156 type_str = optarg;
157 break;
158 case 'v':
159 value = optarg;
160 break;
161 case 'g':
162 guid_str = optarg;
163 break;
164
165 case '?': /* parsing error */
166 print_sub_command_usage(argv[0]);
167 }
168 }
169
170 if (argv[optind] != NULL) {
171 fprintf(stderr, "First unexpected positional argument: %s\n",
172 argv[optind]);
173 print_sub_command_usage(argv[0]);
174 }
175
176 if (name == NULL || value == NULL || type_str == NULL ||
177 guid_str == NULL) {
178 fprintf(stderr, "All options are required\n");
179 print_sub_command_usage(argv[0]);
180 }
181
182 if (name[0] == '\0') {
183 fprintf(stderr, "Variable name can't be empty\n");
184 print_sub_command_usage(argv[0]);
185 }
186
187 EFI_GUID guid;
188 if (!parse_guid(guid_str, &guid)) {
189 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
190 return EXIT_FAILURE;
191 }
192
193 enum data_type type;
194 if (!parse_data_type(type_str, &type)) {
195 fprintf(stderr, "Failed to parse type: %s\n", type_str);
196 return EXIT_FAILURE;
197 }
198
199 size_t data_size;
200 void *data = make_data(value, &data_size, type);
201 if (data == NULL) {
202 fprintf(stderr, "Failed to parse value \"%s\" as %s\n",
203 value, type_str);
204 return EXIT_FAILURE;
205 }
206
207 struct storage_t storage;
208 if (!storage_open(store_file, &storage, /*rw=*/true)) {
209 free(data);
210 return EXIT_FAILURE;
211 }
212
213 struct var_t *var = vs_find(&storage.vs, name, &guid);
214 if (var == NULL) {
215 var = vs_new_var(&storage.vs);
216 var->name = to_uchars(name, &var->name_size);
217 var->data = data;
218 var->data_size = data_size;
219 var->guid = guid;
220 } else {
221 free(var->data);
222 var->data = data;
223 var->data_size = data_size;
224 }
225
226 return storage_write_back(&storage) ? EXIT_SUCCESS : EXIT_FAILURE;
227}
228
229static void help_list(FILE *f, const struct subcommand_t *info)
230{
231 fprintf(f, "List variables in the store:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200232 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200233}
234
235static int process_list(int argc, char *argv[], const char store_file[])
236{
237 if (argc != 1) {
238 fprintf(stderr, "Invalid invocation\n");
239 print_sub_command_usage(argv[0]);
240 }
241
242 struct storage_t storage;
243 if (!storage_open(store_file, &storage, /*rw=*/false))
244 return EXIT_FAILURE;
245
246 for (struct var_t *v = storage.vs.vars; v != NULL; v = v->next) {
247 char *name = to_chars(v->name, v->name_size);
248 char *guid = format_guid(&v->guid, /*use_alias=*/true);
249
250 printf("%-*s:%s (%zu %s)\n", GUID_LEN, guid, name, v->data_size,
251 v->data_size == 1 ? "byte" : "bytes");
252
253 free(name);
254 free(guid);
255 }
256
257 storage_drop(&storage);
258 return EXIT_SUCCESS;
259}
260
261static void help_get(FILE *f, const struct subcommand_t *info)
262{
263 fprintf(f, "Read variable's value:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200264 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200265 fprintf(f, " -g vendor-guid \\\n");
266 fprintf(f, " -n variable-name \\\n");
267 fprintf(f, " -t variable-type\n");
268 fprintf(f, "\n");
269 print_types(f);
270}
271
272static int process_get(int argc, char *argv[], const char store_file[])
273{
274 const char *name = NULL;
275 const char *type_str = NULL;
276 const char *guid_str = NULL;
277 int opt;
278 while ((opt = getopt(argc, argv, "n:g:t:")) != -1) {
279 switch (opt) {
280 case 'n':
281 name = optarg;
282 break;
283 case 'g':
284 guid_str = optarg;
285 break;
286 case 't':
287 type_str = optarg;
288 break;
289
290 case '?': /* parsing error */
291 print_sub_command_usage(argv[0]);
292 }
293 }
294
295 if (name == NULL || type_str == NULL || guid_str == NULL) {
296 fprintf(stderr, "All options are required\n");
297 print_sub_command_usage(argv[0]);
298 }
299
300 EFI_GUID guid;
301 if (!parse_guid(guid_str, &guid)) {
302 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
303 return EXIT_FAILURE;
304 }
305
306 enum data_type type;
307 if (!parse_data_type(type_str, &type)) {
308 fprintf(stderr, "Failed to parse type: %s\n", type_str);
309 return EXIT_FAILURE;
310 }
311
312 struct storage_t storage;
313 if (!storage_open(store_file, &storage, /*rw=*/false))
314 return EXIT_FAILURE;
315
316 int result = EXIT_SUCCESS;
317
318 struct var_t *var = vs_find(&storage.vs, name, &guid);
319 if (var == NULL) {
320 result = EXIT_FAILURE;
321 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
322 guid_str, name);
323 } else if (var->data_size == 0) {
324 fprintf(stderr, "There is no data to show.\n");
325 result = EXIT_FAILURE;
326 } else {
327 print_data(var->data, var->data_size, type);
328 }
329
330 storage_drop(&storage);
331 return result;
332}
333
334static void help_help(FILE *f, const struct subcommand_t *info)
335{
336 fprintf(f, "Display generic help:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200337 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200338 fprintf(f, "\n");
339 fprintf(f, "Display sub-command help:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200340 fprintf(f, " %s smm-store-file|rom %s sub-command\n",
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200341 program_name, info->name);
342}
343
344static int process_help(int argc, char *argv[], const char store_file[])
345{
346 (void)store_file;
347
348 if (argc == 1) {
349 print_help();
350 return EXIT_SUCCESS;
351 }
352
353 if (argc != 2) {
354 fprintf(stderr, "Invalid invocation\n");
355 print_sub_command_usage(argv[0]);
356 return EXIT_FAILURE;
357 }
358
359 const char *sub_command = argv[1];
360
361 for (int i = 0; i < sub_command_count; ++i) {
362 const struct subcommand_t *cmd = &sub_commands[i];
363 if (!str_eq(cmd->name, sub_command))
364 continue;
365
366 cmd->print_help(stdout, cmd);
367 return EXIT_SUCCESS;
368 }
369
370 fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
371 print_help();
372 return EXIT_FAILURE;
373}
374
375static void help_remove(FILE *f, const struct subcommand_t *info)
376{
377 fprintf(f, "Remove a variable:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200378 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200379 fprintf(f, " -g vendor-guid \\\n");
380 fprintf(f, " -n variable-name\n");
381}
382
383static int process_remove(int argc, char *argv[], const char store_file[])
384{
385 const char *name = NULL;
386 const char *guid_str = NULL;
387 int opt;
388 while ((opt = getopt(argc, argv, "n:g:")) != -1) {
389 switch (opt) {
390 case 'n':
391 name = optarg;
392 break;
393 case 'g':
394 guid_str = optarg;
395 break;
396
397 case '?': /* parsing error */
398 print_sub_command_usage(argv[0]);
399 }
400 }
401
402 if (name == NULL || guid_str == NULL) {
403 fprintf(stderr, "All options are required\n");
404 print_sub_command_usage(argv[0]);
405 }
406
407 EFI_GUID guid;
408 if (!parse_guid(guid_str, &guid)) {
409 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
410 return EXIT_FAILURE;
411 }
412
413 struct storage_t storage;
414 if (!storage_open(store_file, &storage, /*rw=*/true))
415 return EXIT_FAILURE;
416
417 int result = EXIT_SUCCESS;
418
419 struct var_t *var = vs_find(&storage.vs, name, &guid);
420 if (var == NULL) {
421 result = EXIT_FAILURE;
422 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
423 guid_str, name);
424 } else {
425 vs_delete(&storage.vs, var);
426 }
427
428 storage_write_back(&storage);
429 return result;
430}
431
432static void help_guids(FILE *f, const struct subcommand_t *info)
433{
434 fprintf(f, "List recognized GUIDS:\n");
Sergii Dmytruk89e056b2024-03-02 17:02:00 +0200435 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name);
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200436}
437
438static int process_guids(int argc, char *argv[], const char store_file[])
439{
440 (void)store_file;
441
442 if (argc != 1) {
443 fprintf(stderr, "Invalid invocation\n");
444 print_sub_command_usage(argv[0]);
445 }
446
447 for (int i = 0; i < known_guid_count; ++i) {
448 char *guid = format_guid(&known_guids[i].guid,
449 /*use_alias=*/false);
450 printf("%-10s -> %s\n", known_guids[i].alias, guid);
451 free(guid);
452 }
453 return EXIT_SUCCESS;
454}
455
456int main(int argc, char *argv[])
457{
458 program_name = argv[0];
459
460 if (argc > 1 && (str_eq(argv[1], "-h") || str_eq(argv[1], "--help"))) {
461 print_help();
462 exit(EXIT_SUCCESS);
463 }
464
465 if (argc < 3)
466 print_program_usage();
467
468 const char *store_file = argv[1];
469 const char *sub_command = argv[2];
470
471 int sub_command_argc = argc - 2;
472 char **sub_command_argv = argv + 2;
473
474 for (int i = 0; i < sub_command_count; ++i) {
475 const struct subcommand_t *cmd = &sub_commands[i];
476 if (!str_eq(cmd->name, sub_command))
477 continue;
478
479 return cmd->process(sub_command_argc,
480 sub_command_argv,
481 store_file);
482 }
483
484 fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
485 print_help();
486 return EXIT_FAILURE;
487}