util: add smmstoretool for editing SMMSTORE

Offline SMMSTORE variable modification tool.  Can be used to
pre-configure ROM image or debug EFI state stored in a dump.

Change-Id: I6c1c06f1d0c39c13b5be76a3070f09b715aca6e0
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/79080
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
This commit is contained in:
Sergii Dmytruk
2023-11-17 19:31:20 +02:00
committed by Martin L Roth
parent 7a51acfbe9
commit 04bd965143
21 changed files with 1730 additions and 0 deletions

View File

@ -144,6 +144,7 @@ from the local git repository for auditing or release `Bash`
Does not show variants. `Shell`
* _ucode_h_to_bin.sh_ - Microcode conversion tool `Bash`
* _update_submodules_ - Check all submodules for updates `Bash`
* __smmstoretool__ - Offline SMMSTORE variable modification tool `C`
* __spdtool__ - Dumps SPD ROMs from a given blob to separate files
using known patterns and reserved bits. Useful for analysing firmware
that holds SPDs on boards that have soldered down DRAM. `python`
@ -171,6 +172,7 @@ the documentation `Bash`
* [cbfstool](util/cbfstool/index.md)
* [ifdtool](util/ifdtool/index.md)
* [intelp2m](util/intelp2m/index.md)
* [smmstoretool](util/smmstoretool/index.md)
## Generated documentation

View File

@ -0,0 +1,126 @@
# smmstoretool
Offline SMMSTORE variable modification tool.
## Operation
### Storage initialization
If SMMSTORE presence isn't detected and an update operation is requested, the
store spanning the whole file is created automatically. Size of the store file
must be a multiple of 64 KiB (block size in version 2 of SMMSTORE protocol),
the variable storage itself will be 64 KiB in size. That's the way EDK2 makes
use of it.
Unlike online editing which mostly appends new variable entries each storage
update with this tool drops all deleted or incomplete entries.
### Unicode
There is no actual support for it. ASCII bytes (or UTF-8 bytes if that was
passed in) is just extended to 16 bits. And Unicode chars that are larger than
8 bit are turned into `?`. Need UTF-8 to/from UTF-16 conversion functions for
proper support.
## Help
Start with:
```
$ smmstoretool -h
Usage: smmstoretool smm-store-file sub-command
smmstoretool -h|--help
Sub-commands:
* get - display current value of a variable
* guids - show GUID to alias mapping
* help - provide built-in help
* list - list variables present in the store
* remove - remove a variable from the store
* set - add or updates a variable in the store
```
Then run `smmstoretool rom help sub-command-name` to get more details.
## Data types
EFI variables in the storage don't have an associated data type and it needs to
be specified on reading/writing variables. Several basic types that correspond
to typical configuration values are supported:
* `bool` (`true`, `false`)
* `uint8` (0-255)
* `uint16` (0-65535)
* `uint32` (0-4294967295)
* `ascii` (NUL-terminated)
* `unicode` (widened and NUL-terminated)
* `raw` (output only; raw bytes on output)
## Examples
`SMMSTORE` is the name of a file containing SMMSTORE data.
### Variable listing
```
$ smmstoretool SMMSTORE list
dasharo :NetworkBoot (1 byte)
c076ec0c-7028-4399-a07271ee5c448b9f:CustomMode (1 byte)
d9bee56e-75dc-49d9-b4d7b534210f637a:certdb (4 bytes)
9073e4e0-60ec-4b6e-99034c223c260f3c:VendorKeysNv (1 byte)
6339d487-26ba-424b-9a5d687e25d740bc:TCG2_DEVICE_DETECTION (1 byte)
6339d487-26ba-424b-9a5d687e25d740bc:TCG2_CONFIGURATION (1 byte)
6339d487-26ba-424b-9a5d687e25d740bc:TCG2_VERSION (16 bytes)
global :Boot0000 (66 bytes)
global :Timeout (2 bytes)
global :PlatformLang (3 bytes)
global :Lang (4 bytes)
global :Key0000 (14 bytes)
global :Boot0001 (102 bytes)
global :Key0001 (14 bytes)
04b37fe8-f6ae-480b-bdd537d98c5e89aa:VarErrorFlag (1 byte)
dasharo :Type1UUID (16 bytes)
dasharo :Type2SN (10 bytes)
global :Boot0002 (90 bytes)
global :BootOrder (8 bytes)
global :Boot0003 (76 bytes)
...
```
### Variable reading
```
$ smmstoretool SMMSTORE get -g dasharo -n UsbDriverStack -t bool
false
```
### Variable writing
```
$ smmstoretool SMMSTORE set -g dasharo -n UsbDriverStack -t bool -v true
```
### Variable deletion
```
$ smmstoretool SMMSTORE remove -g dasharo -n NetworkBoot
```
### Real-world usage
If one edits a newly generated Dasharo `coreboot.rom`:
```bash
cbfstool coreboot.rom read -r SMMSTORE -f SMMSTORE
smmstoretool SMMSTORE set -g dasharo -n NetworkBoot -t bool -v true
cbfstool coreboot.rom write -r SMMSTORE -f SMMSTORE
```
On the first boot, "Network Boot" setting will already be enabled.
Can also read SMMSTORE from a flash and examine some of its contents for
debugging purposes without adding new logging code or powering on the hardware:
```bash
smmstoretool SMMSTORE get -g global -n BootOrder -t raw | hexdump -C
```

View File

@ -133,6 +133,7 @@ from the local git repository for auditing or release `Bash`
Does not show variants. `Shell`
* _ucode_h_to_bin.sh_ - Microcode conversion tool `Bash`
* _update_submodules_ - Check all submodules for updates `Bash`
* __smmstoretool__ - Offline SMMSTORE variable modification tool `C`
* __spdtool__ - Dumps SPD ROMs from a given blob to separate files
using known patterns and reserved bits. Useful for analysing firmware
that holds SPDs on boards that have soldered down DRAM. `python`

1
util/smmstoretool/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
smmstoretool

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0-or-later
PRG := smmstoretool
TOP ?= $(abspath ../..)
ROOT := $(TOP)/src
MDE := $(ROOT)/vendorcode/intel/edk2/UDK2017/MdePkg/Include/
CC ?= $(CROSS_COMPILE)gcc
HOSTCC ?= $(CC)
INSTALL ?= /usr/bin/env install
PREFIX ?= /usr/local
HOSTCFLAGS ?= $(CFLAGS)
HOSTCFLAGS += -Wall -Wextra -MMD -MP -O3
HOSTCFLAGS += -I $(ROOT)/commonlib/bsd/include
HOSTCFLAGS += -I $(ROOT)/vendorcode/intel/edk2/
HOSTCFLAGS += -I $(MDE)
HOSTLDFLAGS ?= $(LDFLAGS)
MACHINE := $(shell uname -m)
ifeq ($(MACHINE),x86_64)
HOSTCFLAGS += -I $(MDE)/X64
else ifeq ($(patsubst i%86,Ia32,$(MACHINE)),Ia32)
HOSTCFLAGS += -I $(MDE)/Ia32
else
$(error Unsupported machine: '$(MACHINE)')
endif
SRC := data.c fv.c guids.c main.c storage.c utils.c vs.c
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.o.d)
.PHONY: all debug clean install
all: $(PRG)
debug: HOSTCFLAGS += -O0 -g
debug: HOSTLDFLAGS += -g
debug: all
install: $(PRG)
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin/
$(INSTALL) $^ $(DESTDIR)$(PREFIX)/bin/
clean:
-$(RM) $(PRG) $(OBJ) $(DEP)
$(PRG): $(OBJ)
$(HOSTCC) -o $@ $^ $(HOSTLDFLAGS)
%.o: %.c
$(HOSTCC) $(HOSTCFLAGS) -c -o $@ -MF $@.d $<
-include $(DEP)

180
util/smmstoretool/data.c Normal file
View File

@ -0,0 +1,180 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "data.h"
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
void print_data(const uint8_t data[], size_t data_size, enum data_type type)
{
if (data_size == 0)
return;
switch (type) {
case DATA_TYPE_BOOL:
bool value = false;
for (size_t i = 0; i < data_size; ++i) {
if (data[i] != 0) {
value = true;
break;
}
}
printf("%s\n", value ? "true" : "false");
break;
case DATA_TYPE_UINT8:
if (data_size != 1) {
fprintf(stderr,
"warning: expected size of 1, got %zu\n",
data_size);
}
if (data_size >= 1)
printf("%u\n", *(uint8_t *)data);
break;
case DATA_TYPE_UINT16:
if (data_size != 2) {
fprintf(stderr,
"warning: expected size of 2, got %zu\n",
data_size);
}
if (data_size >= 2)
printf("%u\n", *(uint16_t *)data);
break;
case DATA_TYPE_UINT32:
if (data_size != 4) {
fprintf(stderr,
"warning: expected size of 4, got %zu\n",
data_size);
}
if (data_size >= 4)
printf("%u\n", *(uint32_t *)data);
break;
case DATA_TYPE_ASCII:
for (size_t i = 0; i < data_size; ++i) {
char c = data[i];
if (isprint(c))
printf("%c", c);
}
printf("\n");
break;
case DATA_TYPE_UNICODE:
char *chars = to_chars((const CHAR16 *)data, data_size);
printf("%s\n", chars);
free(chars);
break;
case DATA_TYPE_RAW:
fwrite(data, 1, data_size, stdout);
break;
}
}
static uint64_t parse_uint(const char source[],
const char type[],
unsigned long long max)
{
char *end;
unsigned long long uint = strtoull(source, &end, /*base=*/0);
if (*end != '\0') {
fprintf(stderr, "Trailing characters in \"%s\": %s\n",
source, end);
return UINT64_MAX;
}
if (uint > max) {
fprintf(stderr, "Invalid %s value: %llu\n", type, uint);
return UINT64_MAX;
}
return uint;
}
void *make_data(const char source[], size_t *data_size, enum data_type type)
{
switch (type) {
void *data;
bool boolean;
unsigned long long uint;
case DATA_TYPE_BOOL:
if (str_eq(source, "true")) {
boolean = true;
} else if (str_eq(source, "false")) {
boolean = false;
} else {
fprintf(stderr, "Invalid boolean value: \"%s\"\n",
source);
return NULL;
}
*data_size = 1;
data = xmalloc(*data_size);
*(uint8_t *)data = boolean;
return data;
case DATA_TYPE_UINT8:
uint = parse_uint(source, "uint8", UINT8_MAX);
if (uint == UINT64_MAX)
return NULL;
*data_size = 1;
data = xmalloc(*data_size);
*(uint8_t *)data = uint;
return data;
case DATA_TYPE_UINT16:
uint = parse_uint(source, "uint16", UINT16_MAX);
if (uint == UINT64_MAX)
return NULL;
*data_size = 2;
data = xmalloc(*data_size);
*(uint16_t *)data = uint;
return data;
case DATA_TYPE_UINT32:
uint = parse_uint(source, "uint32", UINT32_MAX);
if (uint == UINT64_MAX)
return NULL;
*data_size = 4;
data = xmalloc(*data_size);
*(uint32_t *)data = uint;
return data;
case DATA_TYPE_ASCII:
*data_size = strlen(source) + 1;
return strdup(source);
case DATA_TYPE_UNICODE:
return to_uchars(source, data_size);
case DATA_TYPE_RAW:
fprintf(stderr, "Raw data type is output only\n");
return NULL;
}
return NULL;
}
bool parse_data_type(const char str[], enum data_type *type)
{
if (str_eq(str, "bool"))
*type = DATA_TYPE_BOOL;
else if (str_eq(str, "uint8"))
*type = DATA_TYPE_UINT8;
else if (str_eq(str, "uint16"))
*type = DATA_TYPE_UINT16;
else if (str_eq(str, "uint32"))
*type = DATA_TYPE_UINT32;
else if (str_eq(str, "ascii"))
*type = DATA_TYPE_ASCII;
else if (str_eq(str, "unicode"))
*type = DATA_TYPE_UNICODE;
else if (str_eq(str, "raw"))
*type = DATA_TYPE_RAW;
else
return false;
return true;
}

26
util/smmstoretool/data.h Normal file
View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__DATA_H__
#define SMMSTORETOOL__DATA_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
enum data_type {
DATA_TYPE_BOOL,
DATA_TYPE_UINT8,
DATA_TYPE_UINT16,
DATA_TYPE_UINT32,
DATA_TYPE_ASCII,
DATA_TYPE_UNICODE,
DATA_TYPE_RAW,
};
void print_data(const uint8_t data[], size_t data_size, enum data_type type);
void *make_data(const char source[], size_t *data_size, enum data_type type);
bool parse_data_type(const char str[], enum data_type *type);
#endif // SMMSTORETOOL__DATA_H__

View File

@ -0,0 +1 @@
Offline SMMSTORE variable modification tool `C`

175
util/smmstoretool/fv.c Normal file
View File

@ -0,0 +1,175 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "fv.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "udk2017.h"
// The same as in `smmstore.h` header, which isn't in `commonlib`.
#define SMM_BLOCK_SIZE (64*1024)
static const EFI_GUID EfiVariableGuid = EFI_VARIABLE_GUID;
static const EFI_GUID EfiAuthenticatedVariableGuid =
EFI_AUTHENTICATED_VARIABLE_GUID;
static const EFI_GUID EfiSystemNvDataFvGuid = {
0xfff12b8d, 0x7696, 0x4c8b,
{ 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50 }
};
static uint16_t calc_checksum(const uint16_t *hdr, size_t size)
{
assert(size % 2 == 0 && "Header can't have odd length.");
uint16_t checksum = 0;
for (size_t i = 0; i < size / 2; ++i)
checksum += hdr[i];
return checksum;
}
bool
fv_init(struct mem_range_t fv)
{
if (fv.length % SMM_BLOCK_SIZE != 0) {
fprintf(stderr,
"Firmware Volume size is not a multiple of 64KiB\n");
return false;
}
memset(fv.start, 0xff, fv.length);
const EFI_FIRMWARE_VOLUME_HEADER vol_hdr = {
.FileSystemGuid = EfiSystemNvDataFvGuid,
.FvLength = fv.length,
.Signature = EFI_FVH_SIGNATURE,
.Attributes = EFI_FVB2_READ_ENABLED_CAP
| EFI_FVB2_READ_STATUS
| EFI_FVB2_WRITE_ENABLED_CAP
| EFI_FVB2_WRITE_STATUS
| EFI_FVB2_STICKY_WRITE
| EFI_FVB2_MEMORY_MAPPED
| EFI_FVB2_ERASE_POLARITY,
.HeaderLength = sizeof(vol_hdr)
+ sizeof(EFI_FV_BLOCK_MAP_ENTRY),
.Revision = EFI_FVH_REVISION,
.BlockMap[0] = {
.NumBlocks = fv.length / SMM_BLOCK_SIZE,
.Length = SMM_BLOCK_SIZE,
},
};
EFI_FIRMWARE_VOLUME_HEADER *vol_hdr_dst = (void *)fv.start;
*vol_hdr_dst = vol_hdr;
vol_hdr_dst->BlockMap[1].NumBlocks = 0;
vol_hdr_dst->BlockMap[1].Length = 0;
vol_hdr_dst->Checksum =
~calc_checksum((const void *)vol_hdr_dst, vol_hdr.HeaderLength);
++vol_hdr_dst->Checksum;
const VARIABLE_STORE_HEADER var_store_hdr = {
// Authentication-related fields will be filled with 0xff.
.Signature = EfiAuthenticatedVariableGuid,
// Actual size of the storage is block size, the rest is
// Fault Tolerant Write (FTW) space and the FTW spare space.
.Size = SMM_BLOCK_SIZE - vol_hdr.HeaderLength,
.Format = VARIABLE_STORE_FORMATTED,
.State = VARIABLE_STORE_HEALTHY,
};
VARIABLE_STORE_HEADER *var_store_hdr_dst =
(void *)(fv.start + vol_hdr.HeaderLength);
*var_store_hdr_dst = var_store_hdr;
return true;
}
static bool guid_eq(const EFI_GUID *lhs, const EFI_GUID *rhs)
{
return memcmp(lhs, rhs, sizeof(*lhs)) == 0;
}
static bool check_fw_vol_hdr(const EFI_FIRMWARE_VOLUME_HEADER *hdr,
size_t max_size)
{
if (hdr->Revision != EFI_FVH_REVISION ||
hdr->Signature != EFI_FVH_SIGNATURE ||
hdr->FvLength > max_size ||
hdr->HeaderLength > max_size ||
hdr->HeaderLength % 2 != 0) {
fprintf(stderr, "No firmware volume header present\n");
return false;
}
if (!guid_eq(&hdr->FileSystemGuid, &EfiSystemNvDataFvGuid)) {
fprintf(stderr, "Firmware volume GUID non-compatible\n");
return false;
}
uint16_t checksum = calc_checksum((const void *)hdr, hdr->HeaderLength);
if (checksum != 0) {
fprintf(stderr,
"Firmware Volume checksum is non-zero: 0x%04X\n",
checksum);
return false;
}
return true;
}
static bool check_var_store_hdr(const VARIABLE_STORE_HEADER *hdr,
size_t max_size,
bool *auth_vars)
{
*auth_vars = guid_eq(&hdr->Signature, &EfiAuthenticatedVariableGuid);
if (!*auth_vars && !guid_eq(&hdr->Signature, &EfiVariableGuid)) {
fprintf(stderr, "Variable store has unexpected GUID\n");
return false;
}
if (hdr->Size > max_size) {
fprintf(stderr, "Variable store size is too large: %zu > %zu\n",
(size_t)hdr->Size, max_size);
return false;
}
if (hdr->Format != VARIABLE_STORE_FORMATTED) {
fprintf(stderr, "Variable store is not formatted\n");
return false;
}
if (hdr->State != VARIABLE_STORE_HEALTHY) {
fprintf(stderr, "Variable store is not in a healthy state\n");
return false;
}
return true;
}
bool
fv_parse(struct mem_range_t fv, struct mem_range_t *var_store, bool *auth_vars)
{
const EFI_FIRMWARE_VOLUME_HEADER *vol_hdr = (void *)fv.start;
if (!check_fw_vol_hdr(vol_hdr, fv.length)) {
fprintf(stderr, "No valid firmware volume was found\n");
return false;
}
uint8_t *fw_vol_data = fv.start + vol_hdr->HeaderLength;
size_t volume_size = fv.length - vol_hdr->HeaderLength;
const VARIABLE_STORE_HEADER *var_store_hdr = (void *)fw_vol_data;
if (!check_var_store_hdr(var_store_hdr, volume_size, auth_vars)) {
fprintf(stderr, "No valid variable store was found");
return false;
}
var_store->start = fw_vol_data + sizeof(*var_store_hdr);
var_store->length = volume_size - sizeof(*var_store_hdr);
return true;
}

19
util/smmstoretool/fv.h Normal file
View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__FV_H__
#define SMMSTORETOOL__FV_H__
#include <stdbool.h>
#include "utils.h"
// Firmware volume is what's stored in SMMSTORE region of CBFS. It wraps
// variable store.
bool fv_init(struct mem_range_t fv);
bool fv_parse(struct mem_range_t fv,
struct mem_range_t *var_store,
bool *auth_vars);
#endif // SMMSTORETOOL__FV_H__

89
util/smmstoretool/guids.c Normal file
View File

@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "guids.h"
#include <stdio.h>
#include <string.h>
#include <commonlib/bsd/helpers.h>
#include "udk2017.h"
#include "utils.h"
const struct guid_alias_t known_guids[] = {
{
"coreboot",
{
0xceae4c1d, 0x335b, 0x4685,
{ 0xa4, 0xa0, 0xfc, 0x4a, 0x94, 0xee, 0xa0, 0x85 }
},
},
{
"dasharo",
{
0xd15b327e, 0xff2d, 0x4fc1,
{ 0xab, 0xf6, 0xc1, 0x2b, 0xd0, 0x8c, 0x13, 0x59 }
},
},
{
"global",
{
0x8be4df61, 0x93ca, 0x11d2,
{ 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c }
},
},
{
"microsoft",
{
0x77fa9abd, 0x0359, 0x4d32,
{ 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b }
},
},
};
const int known_guid_count = ARRAY_SIZE(known_guids);
char *format_guid(const EFI_GUID *guid, bool use_alias)
{
if (use_alias) {
for (int i = 0; i < known_guid_count; ++i) {
const struct guid_alias_t *known_guid = &known_guids[i];
if (memcmp(&known_guid->guid, guid, sizeof(*guid)) == 0)
return strdup(known_guid->alias);
}
}
char *str = xmalloc(GUID_LEN + 1);
snprintf(str, GUID_LEN + 1,
"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
return str;
}
bool parse_guid(const char str[], EFI_GUID *guid)
{
for (int i = 0; i < known_guid_count; ++i) {
const struct guid_alias_t *known_guid = &known_guids[i];
if (str_eq(known_guid->alias, str)) {
*guid = known_guid->guid;
return true;
}
}
if (strlen(str) != GUID_LEN)
return false;
int n = sscanf(str,
"%08x-%04hx-%04hx-"
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
&guid->Data1, &guid->Data2, &guid->Data3,
&guid->Data4[0], &guid->Data4[1],
&guid->Data4[2], &guid->Data4[3],
&guid->Data4[4], &guid->Data4[5],
&guid->Data4[6], &guid->Data4[7]);
return n == 11;
}

25
util/smmstoretool/guids.h Normal file
View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__GUIDS_H__
#define SMMSTORETOOL__GUIDS_H__
#include <stdbool.h>
#include "udk2017.h"
#define GUID_LEN 35
struct guid_alias_t {
const char *alias;
EFI_GUID guid;
};
extern const struct guid_alias_t known_guids[];
extern const int known_guid_count;
char *format_guid(const EFI_GUID *guid, bool use_alias);
bool parse_guid(const char str[], EFI_GUID *guid);
#endif // SMMSTORETOOL__GUIDS_H__

487
util/smmstoretool/main.c Normal file
View File

@ -0,0 +1,487 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <commonlib/bsd/helpers.h>
#include "data.h"
#include "guids.h"
#include "storage.h"
#include "udk2017.h"
#include "vs.h"
struct subcommand_t {
const char *name;
const char *description;
void (*print_help)(FILE *f, const struct subcommand_t *info);
int (*process)(int argc, char *argv[], const char store_file[]);
};
static void help_get(FILE *f, const struct subcommand_t *info);
static void help_guids(FILE *f, const struct subcommand_t *info);
static void help_help(FILE *f, const struct subcommand_t *info);
static void help_list(FILE *f, const struct subcommand_t *info);
static void help_remove(FILE *f, const struct subcommand_t *info);
static void help_set(FILE *f, const struct subcommand_t *info);
static int process_get(int argc, char *argv[], const char store_file[]);
static int process_guids(int argc, char *argv[], const char store_file[]);
static int process_help(int argc, char *argv[], const char store_file[]);
static int process_list(int argc, char *argv[], const char store_file[]);
static int process_remove(int argc, char *argv[], const char store_file[]);
static int process_set(int argc, char *argv[], const char store_file[]);
static const struct subcommand_t sub_commands[] = {
{
.name = "get",
.description = "display current value of a variable",
.print_help = &help_get,
.process = &process_get,
},
{
.name = "guids",
.description = "show GUID to alias mapping",
.print_help = &help_guids,
.process = &process_guids,
},
{
.name = "help",
.description = "provide built-in help",
.print_help = &help_help,
.process = &process_help,
},
{
.name = "list",
.description = "list variables present in the store",
.print_help = &help_list,
.process = &process_list,
},
{
.name = "remove",
.description = "remove a variable from the store",
.print_help = &help_remove,
.process = &process_remove,
},
{
.name = "set",
.description = "add or updates a variable in the store",
.print_help = &help_set,
.process = &process_set,
},
};
static const int sub_command_count = ARRAY_SIZE(sub_commands);
static const char *USAGE_FMT = "Usage: %s smm-store-file sub-command\n"
" %s -h|--help\n";
static const char *program_name;
static void print_program_usage(void)
{
fprintf(stderr, USAGE_FMT, program_name, program_name);
exit(EXIT_FAILURE);
}
static void print_sub_command_usage(const char sub_command[])
{
fprintf(stderr, "\n");
fprintf(stderr, USAGE_FMT, program_name, program_name);
fprintf(stderr, "\n");
for (int i = 0; i < sub_command_count; ++i) {
const struct subcommand_t *cmd = &sub_commands[i];
if (!str_eq(cmd->name, sub_command))
continue;
cmd->print_help(stderr, cmd);
break;
}
exit(EXIT_FAILURE);
}
static void print_help(void)
{
printf(USAGE_FMT, program_name, program_name);
printf("\n");
printf("Sub-commands:\n");
for (int i = 0; i < sub_command_count; ++i) {
const struct subcommand_t *cmd = &sub_commands[i];
printf(" * %-6s - %s\n", cmd->name, cmd->description);
}
}
static void print_types(FILE *f)
{
fprintf(f, "Types and their values:\n");
fprintf(f, " * bool (true, false)\n");
fprintf(f, " * uint8 (0-255)\n");
fprintf(f, " * ascii (NUL-terminated)\n");
fprintf(f, " * unicode (widened and NUL-terminated)\n");
fprintf(f, " * raw (output only; raw bytes on output)\n");
}
static void help_set(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "Create or update a variable:\n");
fprintf(f, " %s smm-store-file %s \\\n", program_name, info->name);
fprintf(f, " -g vendor-guid \\\n");
fprintf(f, " -n variable-name \\\n");
fprintf(f, " -t variable-type \\\n");
fprintf(f, " -v value\n");
fprintf(f, "\n");
print_types(f);
}
static int process_set(int argc, char *argv[], const char store_file[])
{
const char *name = NULL;
const char *value = NULL;
const char *type_str = NULL;
const char *guid_str = NULL;
int opt;
while ((opt = getopt(argc, argv, "n:t:v:g:")) != -1) {
switch (opt) {
case 'n':
name = optarg;
break;
case 't':
type_str = optarg;
break;
case 'v':
value = optarg;
break;
case 'g':
guid_str = optarg;
break;
case '?': /* parsing error */
print_sub_command_usage(argv[0]);
}
}
if (argv[optind] != NULL) {
fprintf(stderr, "First unexpected positional argument: %s\n",
argv[optind]);
print_sub_command_usage(argv[0]);
}
if (name == NULL || value == NULL || type_str == NULL ||
guid_str == NULL) {
fprintf(stderr, "All options are required\n");
print_sub_command_usage(argv[0]);
}
if (name[0] == '\0') {
fprintf(stderr, "Variable name can't be empty\n");
print_sub_command_usage(argv[0]);
}
EFI_GUID guid;
if (!parse_guid(guid_str, &guid)) {
fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
return EXIT_FAILURE;
}
enum data_type type;
if (!parse_data_type(type_str, &type)) {
fprintf(stderr, "Failed to parse type: %s\n", type_str);
return EXIT_FAILURE;
}
size_t data_size;
void *data = make_data(value, &data_size, type);
if (data == NULL) {
fprintf(stderr, "Failed to parse value \"%s\" as %s\n",
value, type_str);
return EXIT_FAILURE;
}
struct storage_t storage;
if (!storage_open(store_file, &storage, /*rw=*/true)) {
free(data);
return EXIT_FAILURE;
}
struct var_t *var = vs_find(&storage.vs, name, &guid);
if (var == NULL) {
var = vs_new_var(&storage.vs);
var->name = to_uchars(name, &var->name_size);
var->data = data;
var->data_size = data_size;
var->guid = guid;
} else {
free(var->data);
var->data = data;
var->data_size = data_size;
}
return storage_write_back(&storage) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static void help_list(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "List variables in the store:\n");
fprintf(f, " %s smm-store-file %s\n", program_name, info->name);
}
static int process_list(int argc, char *argv[], const char store_file[])
{
if (argc != 1) {
fprintf(stderr, "Invalid invocation\n");
print_sub_command_usage(argv[0]);
}
struct storage_t storage;
if (!storage_open(store_file, &storage, /*rw=*/false))
return EXIT_FAILURE;
for (struct var_t *v = storage.vs.vars; v != NULL; v = v->next) {
char *name = to_chars(v->name, v->name_size);
char *guid = format_guid(&v->guid, /*use_alias=*/true);
printf("%-*s:%s (%zu %s)\n", GUID_LEN, guid, name, v->data_size,
v->data_size == 1 ? "byte" : "bytes");
free(name);
free(guid);
}
storage_drop(&storage);
return EXIT_SUCCESS;
}
static void help_get(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "Read variable's value:\n");
fprintf(f, " %s smm-store-file %s \\\n", program_name, info->name);
fprintf(f, " -g vendor-guid \\\n");
fprintf(f, " -n variable-name \\\n");
fprintf(f, " -t variable-type\n");
fprintf(f, "\n");
print_types(f);
}
static int process_get(int argc, char *argv[], const char store_file[])
{
const char *name = NULL;
const char *type_str = NULL;
const char *guid_str = NULL;
int opt;
while ((opt = getopt(argc, argv, "n:g:t:")) != -1) {
switch (opt) {
case 'n':
name = optarg;
break;
case 'g':
guid_str = optarg;
break;
case 't':
type_str = optarg;
break;
case '?': /* parsing error */
print_sub_command_usage(argv[0]);
}
}
if (name == NULL || type_str == NULL || guid_str == NULL) {
fprintf(stderr, "All options are required\n");
print_sub_command_usage(argv[0]);
}
EFI_GUID guid;
if (!parse_guid(guid_str, &guid)) {
fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
return EXIT_FAILURE;
}
enum data_type type;
if (!parse_data_type(type_str, &type)) {
fprintf(stderr, "Failed to parse type: %s\n", type_str);
return EXIT_FAILURE;
}
struct storage_t storage;
if (!storage_open(store_file, &storage, /*rw=*/false))
return EXIT_FAILURE;
int result = EXIT_SUCCESS;
struct var_t *var = vs_find(&storage.vs, name, &guid);
if (var == NULL) {
result = EXIT_FAILURE;
fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
guid_str, name);
} else if (var->data_size == 0) {
fprintf(stderr, "There is no data to show.\n");
result = EXIT_FAILURE;
} else {
print_data(var->data, var->data_size, type);
}
storage_drop(&storage);
return result;
}
static void help_help(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "Display generic help:\n");
fprintf(f, " %s smm-store-file %s\n", program_name, info->name);
fprintf(f, "\n");
fprintf(f, "Display sub-command help:\n");
fprintf(f, " %s smm-store-file %s sub-command\n",
program_name, info->name);
}
static int process_help(int argc, char *argv[], const char store_file[])
{
(void)store_file;
if (argc == 1) {
print_help();
return EXIT_SUCCESS;
}
if (argc != 2) {
fprintf(stderr, "Invalid invocation\n");
print_sub_command_usage(argv[0]);
return EXIT_FAILURE;
}
const char *sub_command = argv[1];
for (int i = 0; i < sub_command_count; ++i) {
const struct subcommand_t *cmd = &sub_commands[i];
if (!str_eq(cmd->name, sub_command))
continue;
cmd->print_help(stdout, cmd);
return EXIT_SUCCESS;
}
fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
print_help();
return EXIT_FAILURE;
}
static void help_remove(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "Remove a variable:\n");
fprintf(f, " %s smm-store-file %s \\\n", program_name, info->name);
fprintf(f, " -g vendor-guid \\\n");
fprintf(f, " -n variable-name\n");
}
static int process_remove(int argc, char *argv[], const char store_file[])
{
const char *name = NULL;
const char *guid_str = NULL;
int opt;
while ((opt = getopt(argc, argv, "n:g:")) != -1) {
switch (opt) {
case 'n':
name = optarg;
break;
case 'g':
guid_str = optarg;
break;
case '?': /* parsing error */
print_sub_command_usage(argv[0]);
}
}
if (name == NULL || guid_str == NULL) {
fprintf(stderr, "All options are required\n");
print_sub_command_usage(argv[0]);
}
EFI_GUID guid;
if (!parse_guid(guid_str, &guid)) {
fprintf(stderr, "Failed to parse GUID: %s\n", guid_str);
return EXIT_FAILURE;
}
struct storage_t storage;
if (!storage_open(store_file, &storage, /*rw=*/true))
return EXIT_FAILURE;
int result = EXIT_SUCCESS;
struct var_t *var = vs_find(&storage.vs, name, &guid);
if (var == NULL) {
result = EXIT_FAILURE;
fprintf(stderr, "Couldn't find variable \"%s:%s\"\n",
guid_str, name);
} else {
vs_delete(&storage.vs, var);
}
storage_write_back(&storage);
return result;
}
static void help_guids(FILE *f, const struct subcommand_t *info)
{
fprintf(f, "List recognized GUIDS:\n");
fprintf(f, " %s smm-store-file %s\n", program_name, info->name);
}
static int process_guids(int argc, char *argv[], const char store_file[])
{
(void)store_file;
if (argc != 1) {
fprintf(stderr, "Invalid invocation\n");
print_sub_command_usage(argv[0]);
}
for (int i = 0; i < known_guid_count; ++i) {
char *guid = format_guid(&known_guids[i].guid,
/*use_alias=*/false);
printf("%-10s -> %s\n", known_guids[i].alias, guid);
free(guid);
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
program_name = argv[0];
if (argc > 1 && (str_eq(argv[1], "-h") || str_eq(argv[1], "--help"))) {
print_help();
exit(EXIT_SUCCESS);
}
if (argc < 3)
print_program_usage();
const char *store_file = argv[1];
const char *sub_command = argv[2];
int sub_command_argc = argc - 2;
char **sub_command_argv = argv + 2;
for (int i = 0; i < sub_command_count; ++i) {
const struct subcommand_t *cmd = &sub_commands[i];
if (!str_eq(cmd->name, sub_command))
continue;
return cmd->process(sub_command_argc,
sub_command_argv,
store_file);
}
fprintf(stderr, "Unknown sub-command: %s\n", sub_command);
print_help();
return EXIT_FAILURE;
}

View File

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "storage.h"
#include <assert.h>
#include <stdio.h>
#include "fv.h"
#include "utils.h"
bool storage_open(const char store_file[], struct storage_t *storage, bool rw)
{
storage->rw = rw;
storage->file = map_file(store_file, rw);
if (storage->file.start == NULL) {
fprintf(stderr, "Failed to load smm-store-file \"%s\"\n",
store_file);
return false;
}
bool auth_vars;
if (!fv_parse(storage->file, &storage->store_area, &auth_vars)) {
if (!rw) {
fprintf(stderr,
"Failed to find variable store in \"%s\"\n",
store_file);
goto error;
}
if (!fv_init(storage->file)) {
fprintf(stderr,
"Failed to create variable store in \"%s\"\n",
store_file);
goto error;
}
if (!fv_parse(storage->file, &storage->store_area, &auth_vars)) {
fprintf(stderr,
"Failed to parse newly formatted store in \"%s\"\n",
store_file);
goto error;
}
fprintf(stderr,
"Successfully created variable store in \"%s\"\n",
store_file);
}
storage->vs = vs_load(storage->store_area, auth_vars);
return true;
error:
unmap_file(storage->file);
return false;
}
bool storage_write_back(struct storage_t *storage)
{
assert(storage->rw && "Only RW storage can be updated.");
bool success = vs_store(&storage->vs, storage->store_area);
if (!success)
fprintf(stderr, "Failed to update variable store\n");
storage_drop(storage);
return success;
}
void storage_drop(struct storage_t *storage)
{
unmap_file(storage->file);
vs_free(&storage->vs);
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__STORAGE_H__
#define SMMSTORETOOL__STORAGE_H__
#include "vs.h"
#include "utils.h"
struct storage_t {
bool rw;
struct mem_range_t file;
struct mem_range_t store_area;
struct var_store_t vs;
};
bool storage_open(const char store_file[], struct storage_t *storage, bool rw);
bool storage_write_back(struct storage_t *storage);
void storage_drop(struct storage_t *storage);
#endif // SMMSTORETOOL__STORAGE_H__

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__UDK2017_H__
#define SMMSTORETOOL__UDK2017_H__
#include <UDK2017/MdePkg/Include/Uefi/UefiBaseType.h>
#include <UDK2017/MdePkg/Include/Uefi/UefiMultiPhase.h>
#include <UDK2017/MdePkg/Include/Pi/PiFirmwareVolume.h>
#include <UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h>
/*
* ProcessorBind.h contains `#pragma GCC visibility push(hidden)` guarded by an
* identical condition, but there is no corresponding `pop` pragma. This can
* cause trouble for code following headers above including libc headers because
* linker suddenly considers declarations from them (e.g., `strcmp()`) to be
* hidden.
*
* In order to address this situation all UDK2017 headers used by this tool
* must be listed above and included indirectly through this header which
* restores default visibility.
*
* Mind that this issue appears only if the following conditions are satisfied
* and not all toolchains are configured to build position-independent code by
* default (as if -fpic or -fpie appears on compilation command-line).
*/
#if defined(__GNUC__) && defined(__pic__) && !defined(USING_LTO)
#pragma GCC visibility pop
#endif
#endif // SMMSTORETOOL__UDK2017_H__

111
util/smmstoretool/utils.c Normal file
View File

@ -0,0 +1,111 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "utils.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *xmalloc(size_t size)
{
void *p = malloc(size);
if (p == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
abort();
}
return p;
}
char *to_chars(const CHAR16 uchars[], size_t size)
{
char *chars = xmalloc(size / 2 + 1);
const CHAR16 *from = uchars;
char *to = chars;
while (*from != 0) {
CHAR16 uc = *from++;
if (uc < CHAR_MAX)
*to++ = uc;
else
*to++ = '?';
}
// In case there was no terminating NUL.
if (to != chars && to[-1] != '\0')
*to = '\0';
return chars;
}
CHAR16 *to_uchars(const char chars[], size_t *size)
{
*size = (strlen(chars) + 1) * 2;
CHAR16 *uchars = xmalloc(*size);
const char *from = chars;
CHAR16 *to = uchars;
while (*from != '\0')
*to++ = *from++;
*to = 0;
return uchars;
}
bool str_eq(const char lhs[], const char rhs[])
{
return strcmp(lhs, rhs) == 0;
}
struct mem_range_t map_file(const char path[], bool rw)
{
struct mem_range_t range = {0};
int open_flags = rw ? O_RDWR : O_RDONLY;
int mmap_flags = rw ? PROT_READ | PROT_WRITE : PROT_READ;
int fd = open(path, open_flags);
if (fd == -1) {
fprintf(stderr, "Failed to open(): %s\n", strerror(errno));
return range;
}
struct stat stat_buf;
if (fstat(fd, &stat_buf) != 0) {
(void)close(fd);
fprintf(stderr, "Failed to fstat(): %s\n", strerror(errno));
return range;
}
if (stat_buf.st_size == 0) {
(void)close(fd);
fprintf(stderr, "Can't map an empty \"%s\" file\n", path);
return range;
}
uint8_t *mem = mmap(/*addr=*/NULL, stat_buf.st_size, mmap_flags,
MAP_SHARED | MAP_POPULATE, fd, /*offset=*/0);
(void)close(fd);
if (mem == MAP_FAILED) {
fprintf(stderr, "Failed to mmap(): %s\n", strerror(errno));
return range;
}
range.start = mem;
range.length = stat_buf.st_size;
return range;
}
void unmap_file(struct mem_range_t store)
{
if (munmap(store.start, store.length) != 0)
fprintf(stderr, "Failed to munmap(): %s\n", strerror(errno));
}

29
util/smmstoretool/utils.h Normal file
View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__UTILS_H__
#define SMMSTORETOOL__UTILS_H__
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "udk2017.h"
struct mem_range_t {
uint8_t *start;
size_t length;
};
void *xmalloc(size_t size);
char *to_chars(const CHAR16 uchars[], size_t size);
CHAR16 *to_uchars(const char chars[], size_t *size);
bool str_eq(const char lhs[], const char rhs[]);
struct mem_range_t map_file(const char path[], bool rw);
void unmap_file(struct mem_range_t store);
#endif // SMMSTORETOOL__UTILS_H__

232
util/smmstoretool/vs.c Normal file
View File

@ -0,0 +1,232 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "vs.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "udk2017.h"
#include "utils.h"
static size_t get_var_hdr_size(bool auth_vars)
{
if (auth_vars)
return sizeof(AUTHENTICATED_VARIABLE_HEADER);
return sizeof(VARIABLE_HEADER);
}
struct var_store_t vs_load(struct mem_range_t vs_data, bool auth_vars)
{
uint8_t *var_hdr = vs_data.start;
struct var_store_t vs = {
.auth_vars = auth_vars,
.vars = NULL,
};
struct var_t *last_var = NULL;
const size_t var_hdr_size = get_var_hdr_size(auth_vars);
while (var_hdr + var_hdr_size < vs_data.start + vs_data.length) {
uint16_t start_id;
uint8_t state;
struct var_t var = {0};
uint8_t *var_data = var_hdr;
if (auth_vars) {
const AUTHENTICATED_VARIABLE_HEADER *auth_hdr =
(void *)var_data;
start_id = auth_hdr->StartId;
state = auth_hdr->State;
var.reserved = auth_hdr->Reserved;
var.attrs = auth_hdr->Attributes;
var.name_size = auth_hdr->NameSize;
var.data_size = auth_hdr->DataSize;
var.guid = auth_hdr->VendorGuid;
} else {
const VARIABLE_HEADER *no_auth_hdr = (void *)var_data;
start_id = no_auth_hdr->StartId;
state = no_auth_hdr->State;
var.reserved = no_auth_hdr->Reserved;
var.attrs = no_auth_hdr->Attributes;
var.name_size = no_auth_hdr->NameSize;
var.data_size = no_auth_hdr->DataSize;
var.guid = no_auth_hdr->VendorGuid;
}
var_hdr += HEADER_ALIGN(var_hdr_size +
var.name_size +
var.data_size);
if (start_id != VARIABLE_DATA)
break;
if (state != VAR_ADDED)
continue;
if (var.data_size == UINT32_MAX ||
var.name_size == UINT32_MAX ||
var.attrs == UINT32_MAX)
continue;
CHAR16 *name = (void *)(var_data + var_hdr_size);
var.name = xmalloc(var.name_size);
memcpy(var.name, name, var.name_size);
uint8_t *data =
(void *)(var_data + var_hdr_size + var.name_size);
var.data = xmalloc(var.data_size);
memcpy(var.data, data, var.data_size);
struct var_t *var_node = xmalloc(sizeof(*var_node));
*var_node = var;
if (last_var != NULL)
last_var->next = var_node;
else if (vs.vars == NULL)
vs.vars = var_node;
last_var = var_node;
}
return vs;
}
static void store_var(const struct var_t *var, bool auth_vars, uint8_t *data)
{
if (auth_vars) {
AUTHENTICATED_VARIABLE_HEADER hdr;
memset(&hdr, 0xff, sizeof(hdr));
hdr.StartId = VARIABLE_DATA;
hdr.State = VAR_ADDED;
hdr.Reserved = var->reserved;
hdr.Attributes = var->attrs;
hdr.VendorGuid = var->guid;
hdr.NameSize = var->name_size;
hdr.DataSize = var->data_size;
memcpy(data, &hdr, sizeof(hdr));
data += sizeof(hdr);
} else {
VARIABLE_HEADER hdr;
memset(&hdr, 0xff, sizeof(hdr));
hdr.StartId = VARIABLE_DATA;
hdr.State = VAR_ADDED;
hdr.Reserved = var->reserved;
hdr.Attributes = var->attrs;
hdr.VendorGuid = var->guid;
hdr.NameSize = var->name_size;
hdr.DataSize = var->data_size;
memcpy(data, &hdr, sizeof(hdr));
data += sizeof(hdr);
}
memcpy(data, var->name, var->name_size);
memcpy(data + var->name_size, var->data, var->data_size);
}
bool vs_store(struct var_store_t *vs, struct mem_range_t vs_data)
{
uint8_t *out_data = vs_data.start;
const size_t var_hdr_size = get_var_hdr_size(vs->auth_vars);
for (struct var_t *var = vs->vars; var != NULL; var = var->next) {
const size_t var_size =
var_hdr_size + var->name_size + var->data_size;
if (out_data + var_size > vs_data.start + vs_data.length) {
fprintf(stderr,
"Not enough space to serialize Variable Store.\n");
return false;
}
store_var(var, vs->auth_vars, out_data);
out_data += HEADER_ALIGN(var_size);
}
// The rest is "uninitialized".
memset(out_data, 0xff, vs_data.length - (out_data - vs_data.start));
return true;
}
struct var_t *vs_new_var(struct var_store_t *vs)
{
struct var_t *new_var = xmalloc(sizeof(*new_var));
memset(new_var, 0, sizeof(*new_var));
new_var->attrs = EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS;
struct var_t *var = vs->vars;
if (var == NULL) {
vs->vars = new_var;
} else {
while (var->next != NULL)
var = var->next;
var->next = new_var;
}
return new_var;
}
struct var_t *vs_find(struct var_store_t *vs,
const char name[],
const EFI_GUID *guid)
{
size_t name_size;
CHAR16 *uchar_name = to_uchars(name, &name_size);
struct var_t *var;
for (var = vs->vars; var != NULL; var = var->next) {
if (var->name_size != name_size)
continue;
if (memcmp(var->name, uchar_name, name_size) != 0)
continue;
if (memcmp(&var->guid, guid, sizeof(*guid)) != 0)
continue;
break;
}
free(uchar_name);
return var;
}
static void free_var(struct var_t *var)
{
free(var->name);
free(var->data);
free(var);
}
void vs_delete(struct var_store_t *vs, struct var_t *var)
{
if (vs->vars == var) {
vs->vars = var->next;
free_var(var);
return;
}
for (struct var_t *v = vs->vars; v != NULL; v = v->next) {
if (v->next == var) {
v->next = var->next;
free_var(var);
return;
}
}
}
void vs_free(struct var_store_t *vs)
{
for (struct var_t *next, *var = vs->vars; var != NULL; var = next) {
next = var->next;
free_var(var);
}
}

44
util/smmstoretool/vs.h Normal file
View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SMMSTORETOOL__VS_H__
#define SMMSTORETOOL__VS_H__
#include <stdbool.h>
#include "udk2017.h"
#include "utils.h"
// Variable store is part of firmware volume. This unit doesn't deal with its
// header only with data that follows.
struct var_t {
uint8_t reserved;
uint32_t attrs;
EFI_GUID guid;
CHAR16 *name;
size_t name_size; // in bytes
uint8_t *data;
size_t data_size; // in bytes
struct var_t *next;
};
struct var_store_t {
struct var_t *vars;
bool auth_vars;
};
struct var_store_t vs_load(struct mem_range_t vs_data, bool auth_vars);
bool vs_store(struct var_store_t *vs, struct mem_range_t vs_data);
struct var_t *vs_new_var(struct var_store_t *vs);
struct var_t *vs_find(struct var_store_t *vs,
const char name[],
const EFI_GUID *guid);
void vs_delete(struct var_store_t *vs, struct var_t *var);
void vs_free(struct var_store_t *vs);
#endif // SMMSTORETOOL__VS_H__

View File

@ -4,6 +4,7 @@
* [cbfstool](util/cbfstool/index.md)
* [ifdtool](util/ifdtool/index.md)
* [intelp2m](util/intelp2m/index.md)
* [smmstoretool](util/smmstoretool/index.md)
## Generated documentation