util/amdfwtool: Generate hashes for signed AMDFW components
Generate SHA256/SHA384 hash of the signed firmware so that PSP verstage can pass it to PSP. The PSP will use these hashes to verify the integrity of those signed firmwares. BUG=b:203597980 TEST=Build Skyrim BIOS image. Change-Id: I50d278536ba1eac754eb8a39c4c2e428a2371c44 Signed-off-by: Kangheui Won <khwon@chromium.org> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/60290 Reviewed-by: Jon Murphy <jpmurphy@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
bb31562e9e
commit
5b84dfd1c1
|
@ -1,7 +1,11 @@
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
top ?= $(abspath ../..)
|
top ?= $(abspath ../..)
|
||||||
|
ifneq ($(CC),)
|
||||||
|
HOSTCC ?= $(CC)
|
||||||
|
else
|
||||||
HOSTCC ?= cc
|
HOSTCC ?= cc
|
||||||
|
endif
|
||||||
|
|
||||||
READ_SRC = amdfwread.c
|
READ_SRC = amdfwread.c
|
||||||
READ_OBJ = $(READ_SRC:%.c=%.o)
|
READ_OBJ = $(READ_SRC:%.c=%.o)
|
||||||
|
@ -13,16 +17,24 @@ WERROR=-Werror
|
||||||
CFLAGS=-O2 -Wall -Wextra -Wshadow ${WERROR}
|
CFLAGS=-O2 -Wall -Wextra -Wshadow ${WERROR}
|
||||||
CFLAGS += -I $(top)/src/commonlib/bsd/include
|
CFLAGS += -I $(top)/src/commonlib/bsd/include
|
||||||
|
|
||||||
|
ifneq ($(PKG_CONFIG),)
|
||||||
|
HOSTPKGCONFIG ?= $(PKG_CONFIG)
|
||||||
|
else
|
||||||
|
HOSTPKGCONFIG ?= pkg-config
|
||||||
|
endif
|
||||||
|
CFLAGS += $(shell $(HOSTPKGCONFIG) --cflags libcrypto)
|
||||||
|
LDFLAGS += $(shell $(HOSTPKGCONFIG) --libs libcrypto)
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
amdfwread: $(READ_OBJ)
|
amdfwread: $(READ_OBJ)
|
||||||
$(CC) $(READ_OBJ) $(LDFLAGS) -o $@
|
$(HOSTCC) $(READ_OBJ) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
amdfwtool: $(TOOL_OBJ)
|
amdfwtool: $(TOOL_OBJ)
|
||||||
$(CC) $(TOOL_OBJ) $(LDFLAGS) -o $@
|
$(HOSTCC) $(TOOL_OBJ) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
%.o: %.c $(HEADER)
|
%.o: %.c $(HEADER)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(HOSTCC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f $(TARGETS) $(READ_OBJ) $(TOOL_OBJ)
|
@rm -f $(TARGETS) $(READ_OBJ) $(TOOL_OBJ)
|
||||||
|
|
|
@ -5,9 +5,14 @@ amdfwtoolobj = amdfwtool.o data_parse.o
|
||||||
AMDFWTOOLCFLAGS=-O2 -Wall -Wextra -Wshadow -Werror
|
AMDFWTOOLCFLAGS=-O2 -Wall -Wextra -Wshadow -Werror
|
||||||
AMDFWTOOLCFLAGS += -I $(top)/src/commonlib/bsd/include
|
AMDFWTOOLCFLAGS += -I $(top)/src/commonlib/bsd/include
|
||||||
|
|
||||||
|
HOSTPKGCONFIG ?= pkg-config
|
||||||
|
|
||||||
|
AMDFWTOOLCFLAGS += $(shell $(HOSTPKGCONFIG) --cflags libcrypto)
|
||||||
|
LDFLAGS += $(shell $(HOSTPKGCONFIG) --libs libcrypto)
|
||||||
|
|
||||||
$(objutil)/amdfwtool/%.o: $(top)/util/amdfwtool/%.c # $(HEADER)
|
$(objutil)/amdfwtool/%.o: $(top)/util/amdfwtool/%.c # $(HEADER)
|
||||||
$(HOSTCC) $(AMDFWTOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
|
$(HOSTCC) $(AMDFWTOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(objutil)/amdfwtool/amdfwtool: $(addprefix $(objutil)/amdfwtool/,$(amdfwtoolobj))
|
$(objutil)/amdfwtool/amdfwtool: $(addprefix $(objutil)/amdfwtool/,$(amdfwtoolobj))
|
||||||
printf " AMDFWTOOL\n"
|
printf " AMDFWTOOL\n"
|
||||||
$(HOSTCC) $(addprefix $(objutil)/amdfwtool/,$(amdfwtoolobj)) -o $@
|
$(HOSTCC) $(addprefix $(objutil)/amdfwtool/,$(amdfwtoolobj)) $(LDFLAGS) -o $@
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -74,6 +75,17 @@
|
||||||
|
|
||||||
#define DEFAULT_SOFT_FUSE_CHAIN "0x1"
|
#define DEFAULT_SOFT_FUSE_CHAIN "0x1"
|
||||||
|
|
||||||
|
/* Defines related to hashing signed binaries */
|
||||||
|
enum hash_header_ver {
|
||||||
|
HASH_HDR_V1 = 1,
|
||||||
|
};
|
||||||
|
/* Signature ID enums are defined by PSP based on the algorithm used. */
|
||||||
|
enum signature_id {
|
||||||
|
SIG_ID_RSA2048,
|
||||||
|
SIG_ID_RSA4096 = 2,
|
||||||
|
};
|
||||||
|
#define HASH_FILE_SUFFIX ".hash"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Beginning with Family 15h Models 70h-7F, a.k.a Stoney Ridge, the PSP
|
* Beginning with Family 15h Models 70h-7F, a.k.a Stoney Ridge, the PSP
|
||||||
* can support an optional "combo" implementation. If the PSP sees the
|
* can support an optional "combo" implementation. If the PSP sees the
|
||||||
|
@ -655,6 +667,85 @@ static uint16_t get_psp_fw_type(enum platform soc_id, struct amd_fw_header *head
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int add_single_sha(amd_fw_entry_hash *entry, void *buf, enum platform soc_id)
|
||||||
|
{
|
||||||
|
uint8_t hash[SHA384_DIGEST_LENGTH];
|
||||||
|
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||||
|
/* Include only signed part for hash calculation. */
|
||||||
|
size_t len = header->fw_size_signed + sizeof(struct amd_fw_header);
|
||||||
|
uint8_t *body = (uint8_t *)buf;
|
||||||
|
|
||||||
|
if (len > header->size_total)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (header->sig_id == SIG_ID_RSA4096) {
|
||||||
|
SHA384(body, len, hash);
|
||||||
|
entry->sha_len = SHA384_DIGEST_LENGTH;
|
||||||
|
} else if (header->sig_id == SIG_ID_RSA2048) {
|
||||||
|
SHA256(body, len, hash);
|
||||||
|
entry->sha_len = SHA256_DIGEST_LENGTH;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%s: Unknown signature id: 0x%08x\n",
|
||||||
|
__func__, header->sig_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(entry->sha, hash, entry->sha_len);
|
||||||
|
entry->fw_id = get_psp_fw_type(soc_id, header);
|
||||||
|
entry->subtype = header->fw_subtype;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_num_binaries(void *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||||
|
size_t total_len = 0;
|
||||||
|
int num_binaries = 0;
|
||||||
|
|
||||||
|
while (total_len < buf_size) {
|
||||||
|
num_binaries++;
|
||||||
|
total_len += header->size_total;
|
||||||
|
header = (struct amd_fw_header *)(buf + total_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_len != buf_size) {
|
||||||
|
fprintf(stderr, "Malformed binary\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return num_binaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_sha(amd_fw_entry *entry, void *buf, size_t buf_size, enum platform soc_id)
|
||||||
|
{
|
||||||
|
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||||
|
/* Include only signed part for hash calculation. */
|
||||||
|
size_t total_len = 0;
|
||||||
|
int num_binaries = get_num_binaries(buf, buf_size);
|
||||||
|
|
||||||
|
if (num_binaries <= 0)
|
||||||
|
return num_binaries;
|
||||||
|
|
||||||
|
entry->hash_entries = malloc(num_binaries * sizeof(amd_fw_entry_hash));
|
||||||
|
if (!entry->hash_entries) {
|
||||||
|
fprintf(stderr, "Error allocating memory to add FW hash\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
entry->num_hash_entries = num_binaries;
|
||||||
|
|
||||||
|
/* Iterate through each binary */
|
||||||
|
for (int i = 0; i < num_binaries; i++) {
|
||||||
|
if (add_single_sha(&entry->hash_entries[i], buf + total_len, soc_id)) {
|
||||||
|
free(entry->hash_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
total_len += header->size_total;
|
||||||
|
header = (struct amd_fw_header *)(buf + total_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void integrate_firmwares(context *ctx,
|
static void integrate_firmwares(context *ctx,
|
||||||
embedded_firmware *romsig,
|
embedded_firmware *romsig,
|
||||||
amd_fw_entry *fw_table)
|
amd_fw_entry *fw_table)
|
||||||
|
@ -747,6 +838,88 @@ static void free_bdt_firmware_filenames(amd_bios_entry *fw_table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_or_fail(int fd, void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
ssize_t written;
|
||||||
|
|
||||||
|
written = write_from_buf_to_file(fd, ptr, size);
|
||||||
|
if (written < 0 || (size_t)written != size) {
|
||||||
|
fprintf(stderr, "%s: Error writing %zu bytes - written %zd bytes\n",
|
||||||
|
__func__, size, written);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_one_psp_firmware_hash_entry(int fd, amd_fw_entry_hash *entry)
|
||||||
|
{
|
||||||
|
uint16_t type = entry->fw_id;
|
||||||
|
uint16_t subtype = entry->subtype;
|
||||||
|
|
||||||
|
write_or_fail(fd, &type, sizeof(type));
|
||||||
|
write_or_fail(fd, &subtype, sizeof(subtype));
|
||||||
|
write_or_fail(fd, entry->sha, entry->sha_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_psp_firmware_hash(const char *filename,
|
||||||
|
amd_fw_entry *fw_table)
|
||||||
|
{
|
||||||
|
struct psp_fw_hash_table hash_header = {0};
|
||||||
|
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||||
|
filename, strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_header.version = HASH_HDR_V1;
|
||||||
|
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||||
|
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||||
|
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH) {
|
||||||
|
hash_header.no_of_entries_256++;
|
||||||
|
} else if (fw_table[i].hash_entries[j].sha_len ==
|
||||||
|
SHA384_DIGEST_LENGTH) {
|
||||||
|
hash_header.no_of_entries_384++;
|
||||||
|
} else if (fw_table[i].hash_entries[j].sha_len) {
|
||||||
|
fprintf(stderr, "%s: Error invalid sha_len %d\n",
|
||||||
|
__func__, fw_table[i].hash_entries[j].sha_len);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_or_fail(fd, &hash_header, sizeof(hash_header));
|
||||||
|
|
||||||
|
/* Add all the SHA256 hash entries first followed by SHA384 entries. PSP verstage
|
||||||
|
processes the table in that order. Mixing and matching SHA256 and SHA384 entries
|
||||||
|
will cause the hash verification failure at run-time. */
|
||||||
|
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||||
|
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||||
|
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH)
|
||||||
|
write_one_psp_firmware_hash_entry(fd,
|
||||||
|
&fw_table[i].hash_entries[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||||
|
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||||
|
if (fw_table[i].hash_entries[j].sha_len == SHA384_DIGEST_LENGTH)
|
||||||
|
write_one_psp_firmware_hash_entry(fd,
|
||||||
|
&fw_table[i].hash_entries[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||||
|
if (!fw_table[i].num_hash_entries || !fw_table[i].hash_entries)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
free(fw_table[i].hash_entries);
|
||||||
|
fw_table[i].hash_entries = NULL;
|
||||||
|
fw_table[i].num_hash_entries = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
|
* process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
|
||||||
* @signed_rom: Output file path grouping all the signed PSP binaries.
|
* @signed_rom: Output file path grouping all the signed PSP binaries.
|
||||||
|
@ -765,6 +938,8 @@ static void process_signed_psp_firmwares(const char *signed_rom,
|
||||||
int signed_rom_fd;
|
int signed_rom_fd;
|
||||||
ssize_t bytes, align_bytes;
|
ssize_t bytes, align_bytes;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
|
char *signed_rom_hash;
|
||||||
|
size_t signed_rom_hash_strlen;
|
||||||
struct amd_fw_header header;
|
struct amd_fw_header header;
|
||||||
struct stat fd_stat;
|
struct stat fd_stat;
|
||||||
/* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
|
/* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
|
||||||
|
@ -781,6 +956,9 @@ static void process_signed_psp_firmwares(const char *signed_rom,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
for (i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||||
|
fw_table[i].num_hash_entries = 0;
|
||||||
|
fw_table[i].hash_entries = NULL;
|
||||||
|
|
||||||
if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
|
if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -818,10 +996,6 @@ static void process_signed_psp_firmwares(const char *signed_rom,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.size_total != fd_stat.st_size) {
|
|
||||||
close(fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PSP binary is not signed and should not be part of signed PSP binaries
|
/* PSP binary is not signed and should not be part of signed PSP binaries
|
||||||
set. */
|
set. */
|
||||||
|
@ -876,6 +1050,9 @@ static void process_signed_psp_firmwares(const char *signed_rom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (add_sha(&fw_table[i], buf, fd_stat.st_size, soc_id))
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
/* File is successfully processed and is part of signed PSP binaries set. */
|
/* File is successfully processed and is part of signed PSP binaries set. */
|
||||||
fw_table[i].fw_id = get_psp_fw_type(soc_id, &header);
|
fw_table[i].fw_id = get_psp_fw_type(soc_id, &header);
|
||||||
fw_table[i].addr_signed = signed_start_addr;
|
fw_table[i].addr_signed = signed_start_addr;
|
||||||
|
@ -888,6 +1065,18 @@ static void process_signed_psp_firmwares(const char *signed_rom,
|
||||||
}
|
}
|
||||||
|
|
||||||
close(signed_rom_fd);
|
close(signed_rom_fd);
|
||||||
|
|
||||||
|
/* signed_rom file name + ".hash" + '\0' */
|
||||||
|
signed_rom_hash_strlen = strlen(signed_rom) + strlen(HASH_FILE_SUFFIX) + 1;
|
||||||
|
signed_rom_hash = malloc(signed_rom_hash_strlen);
|
||||||
|
if (!signed_rom_hash) {
|
||||||
|
fprintf(stderr, "malloc(%lu) failed\n", signed_rom_hash_strlen);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strcpy(signed_rom_hash, signed_rom);
|
||||||
|
strcat(signed_rom_hash, HASH_FILE_SUFFIX);
|
||||||
|
write_psp_firmware_hash(signed_rom_hash, fw_table);
|
||||||
|
free(signed_rom_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir,
|
static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#define _AMD_FW_TOOL_H_
|
#define _AMD_FW_TOOL_H_
|
||||||
|
|
||||||
#include <commonlib/bsd/compiler.h>
|
#include <commonlib/bsd/compiler.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
@ -273,6 +274,14 @@ typedef struct _ish_directory_table {
|
||||||
#define PSP_LVL2_AB (1 << 3)
|
#define PSP_LVL2_AB (1 << 3)
|
||||||
#define PSP_BOTH (PSP_LVL1 | PSP_LVL2)
|
#define PSP_BOTH (PSP_LVL1 | PSP_LVL2)
|
||||||
#define PSP_BOTH_AB (PSP_LVL1_AB | PSP_LVL2_AB)
|
#define PSP_BOTH_AB (PSP_LVL1_AB | PSP_LVL2_AB)
|
||||||
|
|
||||||
|
typedef struct _amd_fw_entry_hash {
|
||||||
|
uint16_t fw_id;
|
||||||
|
uint16_t subtype;
|
||||||
|
uint32_t sha_len;
|
||||||
|
uint8_t sha[SHA384_DIGEST_LENGTH];
|
||||||
|
} amd_fw_entry_hash;
|
||||||
|
|
||||||
typedef struct _amd_fw_entry {
|
typedef struct _amd_fw_entry {
|
||||||
amd_fw_type type;
|
amd_fw_type type;
|
||||||
/* Mendocino and later SoCs use fw_id instead of fw_type. fw_type is still around
|
/* Mendocino and later SoCs use fw_id instead of fw_type. fw_type is still around
|
||||||
|
@ -292,6 +301,8 @@ typedef struct _amd_fw_entry {
|
||||||
/* Some files that don't have amd_fw_header have to be skipped from hashing. These files
|
/* Some files that don't have amd_fw_header have to be skipped from hashing. These files
|
||||||
include but not limited to: *iKek*, *.tkn, *.stkn */
|
include but not limited to: *iKek*, *.tkn, *.stkn */
|
||||||
bool skip_hashing;
|
bool skip_hashing;
|
||||||
|
uint32_t num_hash_entries;
|
||||||
|
amd_fw_entry_hash *hash_entries;
|
||||||
} amd_fw_entry;
|
} amd_fw_entry;
|
||||||
|
|
||||||
/* Most PSP binaries, if not all, have the following header format. */
|
/* Most PSP binaries, if not all, have the following header format. */
|
||||||
|
@ -321,6 +332,14 @@ struct amd_fw_header {
|
||||||
uint8_t reserved_80[128];
|
uint8_t reserved_80[128];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct psp_fw_hash_table {
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t no_of_entries_256;
|
||||||
|
uint16_t no_of_entries_384;
|
||||||
|
/* The next 2 elements are pointers to arrays of SHA256 and SHA384 entries. */
|
||||||
|
/* It does not make sense to store pointers in the CBFS file */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
typedef struct _amd_cb_config {
|
typedef struct _amd_cb_config {
|
||||||
bool have_whitelist;
|
bool have_whitelist;
|
||||||
bool unlock_secure;
|
bool unlock_secure;
|
||||||
|
|
Loading…
Reference in New Issue