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