mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-06-09 23:36:03 +09:00
Pull request for UEFI sub-system for next
* UEFI capsule authentication * UEFI capsule update on QEMU ARM * fsuuid command for FAT file system * bug fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEbcT5xx8ppvoGt20zxIHbvCwFGsQFAl/t+U4ACgkQxIHbvCwF GsTexw/+K0BXWsx2lSBuOty0QIazr9ZjljVYLHIWa9PuWibEWPlfDZFLVSXZmYJ2 nv7B8ieagO8Ev+ULog8Y64q0Ild8188J/00zhWHRe96ovIjjyZ60jJPHUeUlL9UY XJdpKPUyYdtJEG8I8DUaFiuGpleidNLScnEhGdpyJXvyW5vV9helcpSYexB/jOCe LTgFR6a4ULpTQUpbKPEBlhF7Ww4/jJbVT8rdmACRGGvNMBI65wbvTHcQ2fZbi8MS gau7eUd9rBCn/SbBKv19rGCffKvksBxXGfmp/+neB7ZUNwf2suQqVmDkucSATw93 fUyZCbyMm1f5WfhmJK7EfF8xUE4v5gBwrmCuV7TARzX9dRJeslezd5OVK+WpniVM ZhRt7dBXdmbpf35o2KyPuvI//+Sa8vJw80vtiSK/UZCH5uEAkDK9+VXzQiRna7dN tNmFW1bJEYz18lf6n9KWLH4A8mustwi2mwHS5sk+m8pYpS8Cihmwhpmv++QvSEyg RvmS1ax77Bj3krfqVCiFQixuRmcwYjwPySvTzjFVJzVlEiRgoYo7BIHo6bZx8aDh 3lkB7By2OyC2xzRyd0Lkxw497tMaEQMuFV9voRuT7jmdK7g0grnu1daapKM5wKnt wKxoZ8XmFKw2hyTnlNvkg8CP+B4lPPB2AEyf4q21iBM/bfOawo4= =d+Lk -----END PGP SIGNATURE----- Merge tag 'efi-next' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi into next Pull request for UEFI sub-system for next * UEFI capsule authentication * UEFI capsule update on QEMU ARM * fsuuid command for FAT file system * bug fixes
This commit is contained in:
commit
c86b18074c
|
@ -16,12 +16,14 @@ choice
|
||||||
config TARGET_QEMU_ARM_32BIT
|
config TARGET_QEMU_ARM_32BIT
|
||||||
bool "ARMv7-A, 32bit"
|
bool "ARMv7-A, 32bit"
|
||||||
select ARCH_SUPPORT_PSCI
|
select ARCH_SUPPORT_PSCI
|
||||||
|
select BOARD_LATE_INIT
|
||||||
select CPU_V7A
|
select CPU_V7A
|
||||||
select SYS_ARCH_TIMER
|
select SYS_ARCH_TIMER
|
||||||
|
|
||||||
config TARGET_QEMU_ARM_64BIT
|
config TARGET_QEMU_ARM_64BIT
|
||||||
bool "ARMv8, 64bit"
|
bool "ARMv8, 64bit"
|
||||||
select ARM64
|
select ARM64
|
||||||
|
select BOARD_LATE_INIT
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
#ifndef __FSP_SUPPORT_H__
|
#ifndef __FSP_SUPPORT_H__
|
||||||
#define __FSP_SUPPORT_H__
|
#define __FSP_SUPPORT_H__
|
||||||
|
|
||||||
|
#include <signatures.h>
|
||||||
|
|
||||||
#include <asm/fsp/fsp_bootmode.h>
|
#include <asm/fsp/fsp_bootmode.h>
|
||||||
#include <asm/fsp/fsp_fv.h>
|
#include <asm/fsp/fsp_fv.h>
|
||||||
#include <asm/fsp/fsp_hob.h>
|
#include <asm/fsp/fsp_hob.h>
|
||||||
#include <asm/fsp/fsp_infoheader.h>
|
#include <asm/fsp/fsp_infoheader.h>
|
||||||
#include <asm/fsp/fsp_types.h>
|
|
||||||
#include <asm/fsp_arch.h>
|
#include <asm/fsp_arch.h>
|
||||||
#include <asm/fsp/fsp_azalia.h>
|
#include <asm/fsp/fsp_azalia.h>
|
||||||
|
|
||||||
|
|
15
board/emulation/common/Kconfig
Normal file
15
board/emulation/common/Kconfig
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
config MTDPARTS_NOR0
|
||||||
|
string "mtd boot partition for nor0"
|
||||||
|
default "64m(u-boot)" if TARGET_QEMU_ARM_64BIT && !TFABOOT
|
||||||
|
depends on SYS_MTDPARTS_RUNTIME
|
||||||
|
help
|
||||||
|
This define the partition of nor0 used to build mtparts dynamically
|
||||||
|
for boot from nor0.
|
||||||
|
|
||||||
|
config MTDPARTS_NOR1
|
||||||
|
string "mtd u-boot env partition for nor1"
|
||||||
|
default "64m(u-boot-env)" if TARGET_QEMU_ARM_64BIT && !TFABOOT
|
||||||
|
depends on SYS_MTDPARTS_RUNTIME
|
||||||
|
help
|
||||||
|
This define the partition of nor1 used to build mtparts dynamically
|
||||||
|
for the u-boot env stored on nor1.
|
5
board/emulation/common/Makefile
Normal file
5
board/emulation/common/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
obj-$(CONFIG_SYS_MTDPARTS_RUNTIME) += qemu_mtdparts.o
|
||||||
|
obj-$(CONFIG_SET_DFU_ALT_INFO) += qemu_dfu.o
|
||||||
|
obj-$(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT) += qemu_capsule.o
|
48
board/emulation/common/qemu_capsule.c
Normal file
48
board/emulation/common/qemu_capsule.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Linaro Limited
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <efi_api.h>
|
||||||
|
#include <efi_loader.h>
|
||||||
|
#include <env.h>
|
||||||
|
#include <fdtdec.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
int efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
|
||||||
|
{
|
||||||
|
const void *fdt_blob = gd->fdt_blob;
|
||||||
|
const void *blob;
|
||||||
|
const char *cnode_name = "capsule-key";
|
||||||
|
const char *snode_name = "signature";
|
||||||
|
int sig_node;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
|
||||||
|
if (sig_node < 0) {
|
||||||
|
EFI_PRINT("Unable to get signature node offset\n");
|
||||||
|
return -FDT_ERR_NOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
|
||||||
|
|
||||||
|
if (!blob || len < 0) {
|
||||||
|
EFI_PRINT("Unable to get capsule-key value\n");
|
||||||
|
*pkey = NULL;
|
||||||
|
*pkey_len = 0;
|
||||||
|
return -FDT_ERR_NOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pkey = (void *)blob;
|
||||||
|
*pkey_len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool efi_capsule_auth_enabled(void)
|
||||||
|
{
|
||||||
|
return env_get("capsule_authentication_enabled") != NULL ?
|
||||||
|
true : false;
|
||||||
|
}
|
68
board/emulation/common/qemu_dfu.c
Normal file
68
board/emulation/common/qemu_dfu.c
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Linaro Limited
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <dfu.h>
|
||||||
|
#include <env.h>
|
||||||
|
#include <memalign.h>
|
||||||
|
#include <mtd.h>
|
||||||
|
|
||||||
|
#define DFU_ALT_BUF_LEN SZ_1K
|
||||||
|
|
||||||
|
static void board_get_alt_info(struct mtd_info *mtd, char *buf)
|
||||||
|
{
|
||||||
|
struct mtd_info *part;
|
||||||
|
bool first = true;
|
||||||
|
const char *name;
|
||||||
|
int len, partnum = 0;
|
||||||
|
|
||||||
|
name = mtd->name;
|
||||||
|
len = strlen(buf);
|
||||||
|
|
||||||
|
if (buf[0] != '\0')
|
||||||
|
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, "&");
|
||||||
|
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
|
||||||
|
"mtd %s=", name);
|
||||||
|
|
||||||
|
list_for_each_entry(part, &mtd->partitions, node) {
|
||||||
|
partnum++;
|
||||||
|
if (!first)
|
||||||
|
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, ";");
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
|
||||||
|
"%s part %d",
|
||||||
|
part->name, partnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_dfu_alt_info(char *interface, char *devstr)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
|
||||||
|
ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
|
||||||
|
|
||||||
|
if (env_get("dfu_alt_info"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Currently dfu_alt_info is needed on Qemu ARM64 for
|
||||||
|
* capsule updates
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT) &&
|
||||||
|
IS_ENABLED(CONFIG_TARGET_QEMU_ARM_64BIT)) {
|
||||||
|
/* probe all MTD devices */
|
||||||
|
mtd_probe_devices();
|
||||||
|
|
||||||
|
mtd = get_mtd_device_nm("nor0");
|
||||||
|
if (!IS_ERR_OR_NULL(mtd))
|
||||||
|
board_get_alt_info(mtd, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
env_set("dfu_alt_info", buf);
|
||||||
|
printf("dfu_alt_info set\n");
|
||||||
|
}
|
82
board/emulation/common/qemu_mtdparts.c
Normal file
82
board/emulation/common/qemu_mtdparts.c
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Linaro Limited
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <mtd.h>
|
||||||
|
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
|
#define MTDPARTS_LEN 256
|
||||||
|
#define MTDIDS_LEN 128
|
||||||
|
|
||||||
|
static void board_get_mtdparts(const char *dev, const char *partition,
|
||||||
|
char *mtdids, char *mtdparts)
|
||||||
|
{
|
||||||
|
/* mtdids: "<dev>=<dev>, ...." */
|
||||||
|
if (mtdids[0] != '\0')
|
||||||
|
strcat(mtdids, ",");
|
||||||
|
strcat(mtdids, dev);
|
||||||
|
strcat(mtdids, "=");
|
||||||
|
strcat(mtdids, dev);
|
||||||
|
|
||||||
|
/* mtdparts: "mtdparts=<dev>:<mtdparts_<dev>>;..." */
|
||||||
|
if (mtdparts[0] != '\0')
|
||||||
|
strncat(mtdparts, ";", MTDPARTS_LEN);
|
||||||
|
else
|
||||||
|
strcat(mtdparts, "mtdparts=");
|
||||||
|
|
||||||
|
strncat(mtdparts, dev, MTDPARTS_LEN);
|
||||||
|
strncat(mtdparts, ":", MTDPARTS_LEN);
|
||||||
|
strncat(mtdparts, partition, MTDPARTS_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_mtdparts_default(const char **mtdids, const char **mtdparts)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct udevice *dev;
|
||||||
|
const char *mtd_partition;
|
||||||
|
static char parts[3 * MTDPARTS_LEN + 1];
|
||||||
|
static char ids[MTDIDS_LEN + 1];
|
||||||
|
static bool mtd_initialized;
|
||||||
|
|
||||||
|
if (mtd_initialized) {
|
||||||
|
*mtdids = ids;
|
||||||
|
*mtdparts = parts;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(parts, 0, sizeof(parts));
|
||||||
|
memset(ids, 0, sizeof(ids));
|
||||||
|
|
||||||
|
/* Currently mtdparts is needed on Qemu ARM64 for capsule updates */
|
||||||
|
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT) &&
|
||||||
|
IS_ENABLED(CONFIG_TARGET_QEMU_ARM_64BIT)) {
|
||||||
|
/* probe all MTD devices */
|
||||||
|
for (uclass_first_device(UCLASS_MTD, &dev); dev;
|
||||||
|
uclass_next_device(&dev)) {
|
||||||
|
debug("mtd device = %s\n", dev->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
mtd = get_mtd_device_nm("nor0");
|
||||||
|
if (!IS_ERR_OR_NULL(mtd)) {
|
||||||
|
mtd_partition = CONFIG_MTDPARTS_NOR0;
|
||||||
|
board_get_mtdparts("nor0", mtd_partition, ids, parts);
|
||||||
|
put_mtd_device(mtd);
|
||||||
|
}
|
||||||
|
|
||||||
|
mtd = get_mtd_device_nm("nor1");
|
||||||
|
if (!IS_ERR_OR_NULL(mtd)) {
|
||||||
|
mtd_partition = CONFIG_MTDPARTS_NOR1;
|
||||||
|
board_get_mtdparts("nor1", mtd_partition, ids, parts);
|
||||||
|
put_mtd_device(mtd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mtd_initialized = true;
|
||||||
|
*mtdids = ids;
|
||||||
|
*mtdparts = parts;
|
||||||
|
debug("%s:mtdids=%s & mtdparts=%s\n", __func__, ids, parts);
|
||||||
|
}
|
|
@ -11,3 +11,11 @@ config BOARD_SPECIFIC_OPTIONS # dummy
|
||||||
imply VIRTIO_BLK
|
imply VIRTIO_BLK
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if TARGET_QEMU_ARM_64BIT && !TFABOOT
|
||||||
|
config BOARD_SPECIFIC_OPTIONS
|
||||||
|
imply SYS_MTDPARTS_RUNTIME
|
||||||
|
imply SET_DFU_ALT_INFO
|
||||||
|
|
||||||
|
source "board/emulation/common/Kconfig"
|
||||||
|
endif
|
||||||
|
|
|
@ -64,6 +64,11 @@ struct mm_region *mem_map = qemu_arm64_mem_map;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int board_init(void)
|
int board_init(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int board_late_init(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Make sure virtio bus is enumerated so that peripherals
|
* Make sure virtio bus is enumerated so that peripherals
|
||||||
|
|
|
@ -79,6 +79,16 @@ static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag,
|
||||||
return CMD_RET_SUCCESS;
|
return CMD_RET_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_efi_capsule_on_disk_update(struct cmd_tbl *cmdtp, int flag,
|
||||||
|
int argc, char * const argv[])
|
||||||
|
{
|
||||||
|
efi_status_t ret;
|
||||||
|
|
||||||
|
ret = efi_launch_capsules();
|
||||||
|
|
||||||
|
return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do_efi_capsule_show() - show capsule information
|
* do_efi_capsule_show() - show capsule information
|
||||||
*
|
*
|
||||||
|
@ -207,6 +217,8 @@ static struct cmd_tbl cmd_efidebug_capsule_sub[] = {
|
||||||
"", ""),
|
"", ""),
|
||||||
U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
|
U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
|
||||||
"", ""),
|
"", ""),
|
||||||
|
U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
|
||||||
|
"", ""),
|
||||||
U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
|
U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
|
||||||
"", ""),
|
"", ""),
|
||||||
};
|
};
|
||||||
|
@ -1544,6 +1556,8 @@ static char efidebug_help_text[] =
|
||||||
#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
|
#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
|
||||||
"efidebug capsule update [-v] <capsule address>\n"
|
"efidebug capsule update [-v] <capsule address>\n"
|
||||||
" - process a capsule\n"
|
" - process a capsule\n"
|
||||||
|
"efidebug capsule disk-update\n"
|
||||||
|
" - update a capsule from disk\n"
|
||||||
"efidebug capsule show <capsule address>\n"
|
"efidebug capsule show <capsule address>\n"
|
||||||
" - show capsule information\n"
|
" - show capsule information\n"
|
||||||
"efidebug capsule result [<capsule result var>]\n"
|
"efidebug capsule result [<capsule result var>]\n"
|
||||||
|
|
|
@ -10,3 +10,4 @@ Emulation
|
||||||
qemu-mips
|
qemu-mips
|
||||||
qemu-riscv
|
qemu-riscv
|
||||||
qemu-x86
|
qemu-x86
|
||||||
|
qemu_capsule_update
|
||||||
|
|
210
doc/board/emulation/qemu_capsule_update.rst
Normal file
210
doc/board/emulation/qemu_capsule_update.rst
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
|
.. Copyright (C) 2020, Linaro Limited
|
||||||
|
|
||||||
|
Enabling UEFI Capsule Update feature
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Support has been added for the UEFI capsule update feature which
|
||||||
|
enables updating the U-Boot image using the UEFI firmware management
|
||||||
|
protocol (fmp). The capsules are not passed to the firmware through
|
||||||
|
the UpdateCapsule runtime service. Instead, capsule-on-disk
|
||||||
|
functionality is used for fetching the capsule from the EFI System
|
||||||
|
Partition (ESP) by placing the capsule file under the
|
||||||
|
\EFI\UpdateCapsule directory.
|
||||||
|
|
||||||
|
Currently, support has been added on the QEMU ARM64 virt platform for
|
||||||
|
updating the U-Boot binary as a raw image when the platform is booted
|
||||||
|
in non-secure mode, i.e. with CONFIG_TFABOOT disabled. For this
|
||||||
|
configuration, the QEMU platform needs to be booted with
|
||||||
|
'secure=off'. The U-Boot binary placed on the first bank of the NOR
|
||||||
|
flash at offset 0x0. The U-Boot environment is placed on the second
|
||||||
|
NOR flash bank at offset 0x4000000.
|
||||||
|
|
||||||
|
The capsule update feature is enabled with the following configuration
|
||||||
|
settings::
|
||||||
|
|
||||||
|
CONFIG_MTD=y
|
||||||
|
CONFIG_FLASH_CFI_MTD=y
|
||||||
|
CONFIG_CMD_MTDPARTS=y
|
||||||
|
CONFIG_CMD_DFU=y
|
||||||
|
CONFIG_DFU_MTD=y
|
||||||
|
CONFIG_PCI_INIT_R=y
|
||||||
|
CONFIG_EFI_CAPSULE_ON_DISK=y
|
||||||
|
CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y
|
||||||
|
CONFIG_EFI_CAPSULE_FIRMWARE=y
|
||||||
|
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
|
||||||
|
CONFIG_EFI_CAPSULE_FMP_HEADER=y
|
||||||
|
|
||||||
|
In addition, the following config needs to be disabled(QEMU ARM specific)::
|
||||||
|
|
||||||
|
CONFIG_TFABOOT
|
||||||
|
|
||||||
|
The capsule file can be generated by using the GenerateCapsule.py
|
||||||
|
script in EDKII::
|
||||||
|
|
||||||
|
$ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
|
||||||
|
<capsule_file_name> --fw-version <val> --lsv <val> --guid \
|
||||||
|
e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose --update-image-index \
|
||||||
|
<val> --verbose <u-boot.bin>
|
||||||
|
|
||||||
|
The above is a wrapper script(GenerateCapsule) which eventually calls
|
||||||
|
the actual GenerateCapsule.py script.
|
||||||
|
|
||||||
|
As per the UEFI specification, the capsule file needs to be placed on
|
||||||
|
the EFI System Partition, under the \EFI\UpdateCapsule directory. The
|
||||||
|
EFI System Partition can be a virtio-blk-device.
|
||||||
|
|
||||||
|
Before initiating the firmware update, the efi variables BootNext,
|
||||||
|
BootXXXX and OsIndications need to be set. The BootXXXX variable needs
|
||||||
|
to be pointing to the EFI System Partition which contains the capsule
|
||||||
|
file. The BootNext, BootXXXX and OsIndications variables can be set
|
||||||
|
using the following commands::
|
||||||
|
|
||||||
|
=> efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
|
||||||
|
=> efidebug boot next 0
|
||||||
|
=> setenv -e -nv -bs -rt -v OsIndications =0x04
|
||||||
|
=> saveenv
|
||||||
|
|
||||||
|
Finally, the capsule update can be initiated with the following
|
||||||
|
command::
|
||||||
|
|
||||||
|
=> efidebug capsule disk-update
|
||||||
|
|
||||||
|
The updated U-Boot image will be booted on subsequent boot.
|
||||||
|
|
||||||
|
Enabling Capsule Authentication
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The UEFI specification defines a way of authenticating the capsule to
|
||||||
|
be updated by verifying the capsule signature. The capsule signature
|
||||||
|
is computed and prepended to the capsule payload at the time of
|
||||||
|
capsule generation. This signature is then verified by using the
|
||||||
|
public key stored as part of the X509 certificate. This certificate is
|
||||||
|
in the form of an efi signature list (esl) file, which is embedded as
|
||||||
|
part of the platform's device tree blob using the mkeficapsule
|
||||||
|
utility.
|
||||||
|
|
||||||
|
On the QEMU virt platforms, the device-tree is generated on the fly
|
||||||
|
based on the devices configured. This device tree is then passed on to
|
||||||
|
the various software components booting on the platform, including
|
||||||
|
U-Boot. Therefore, on the QEMU virt platform, the signatute is
|
||||||
|
embedded on an overlay. This overlay is then applied at runtime to the
|
||||||
|
base platform device-tree. Steps needed for embedding the esl file in
|
||||||
|
the overlay are highlighted below.
|
||||||
|
|
||||||
|
The capsule authentication feature can be enabled through the
|
||||||
|
following config, in addition to the configs listed above for capsule
|
||||||
|
update::
|
||||||
|
|
||||||
|
CONFIG_EFI_CAPSULE_AUTHENTICATE=y
|
||||||
|
|
||||||
|
The public and private keys used for the signing process are generated
|
||||||
|
and used by the steps highlighted below::
|
||||||
|
|
||||||
|
1. Install utility commands on your host
|
||||||
|
* OPENSSL
|
||||||
|
* efitools
|
||||||
|
|
||||||
|
2. Create signing keys and certificate files on your host
|
||||||
|
|
||||||
|
$ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ \
|
||||||
|
-keyout CRT.key -out CRT.crt -nodes -days 365
|
||||||
|
$ cert-to-efi-sig-list CRT.crt CRT.esl
|
||||||
|
|
||||||
|
$ openssl x509 -in CRT.crt -out CRT.cer -outform DER
|
||||||
|
$ openssl x509 -inform DER -in CRT.cer -outform PEM -out CRT.pub.pem
|
||||||
|
|
||||||
|
$ openssl pkcs12 -export -out CRT.pfx -inkey CRT.key -in CRT.crt
|
||||||
|
$ openssl pkcs12 -in CRT.pfx -nodes -out CRT.pem
|
||||||
|
|
||||||
|
The capsule file can be generated by using the GenerateCapsule.py
|
||||||
|
script in EDKII::
|
||||||
|
|
||||||
|
$ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
|
||||||
|
<capsule_file_name> --monotonic-count <val> --fw-version \
|
||||||
|
<val> --lsv <val> --guid \
|
||||||
|
e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose \
|
||||||
|
--update-image-index <val> --signer-private-cert \
|
||||||
|
/path/to/CRT.pem --trusted-public-cert \
|
||||||
|
/path/to/CRT.pub.pem --other-public-cert /path/to/CRT.pub.pem \
|
||||||
|
<u-boot.bin>
|
||||||
|
|
||||||
|
Place the capsule generated in the above step on the EFI System
|
||||||
|
Partition under the EFI/UpdateCapsule directory
|
||||||
|
|
||||||
|
For embedding the public key certificate, the following steps need to
|
||||||
|
be followed::
|
||||||
|
|
||||||
|
1. Generate a skeleton overlay dts file, with a single fragment
|
||||||
|
node and an empty __overlay__ node
|
||||||
|
|
||||||
|
A typical skeleton overlay file will look like this
|
||||||
|
|
||||||
|
/dts-v1/;
|
||||||
|
/plugin/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
fragment@0 {
|
||||||
|
target-path = "/";
|
||||||
|
__overlay__ {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
2. Convert the dts to a corresponding dtb with the following
|
||||||
|
command
|
||||||
|
./scripts/dtc/dtc -@ -I dts -O dtb -o <ov_dtb_file_name> \
|
||||||
|
<dts_file>
|
||||||
|
|
||||||
|
3. Run the dtb file generated above through the mkeficapsule tool
|
||||||
|
in U-Boot
|
||||||
|
./tools/mkeficapsule -O <pub_key.esl> -D <ov_dtb>
|
||||||
|
|
||||||
|
Running the above command results in the creation of a 'signature'
|
||||||
|
node in the dtb, under which the public key is stored as a
|
||||||
|
'capsule-key' property. The '-O' option is to be used since the
|
||||||
|
public key certificate(esl) file is being embedded in an overlay.
|
||||||
|
|
||||||
|
The dtb file embedded with the certificate is now to be placed on an
|
||||||
|
EFI System Partition. This would then be loaded and "merged" with the
|
||||||
|
base platform flattened device-tree(dtb) at runtime.
|
||||||
|
|
||||||
|
Build U-Boot with the following steps(QEMU ARM64)::
|
||||||
|
|
||||||
|
$ make qemu_arm64_defconfig
|
||||||
|
$ make menuconfig
|
||||||
|
Disable CONFIG_TFABOOT
|
||||||
|
Enable CONFIG_EFI_CAPSULE_AUTHENTICATE
|
||||||
|
Enable all configs needed for capsule update(listed above)
|
||||||
|
$ make all
|
||||||
|
|
||||||
|
Boot the platform and perform the following steps on the U-Boot
|
||||||
|
command line::
|
||||||
|
|
||||||
|
1. Enable capsule authentication by setting the following env
|
||||||
|
variable
|
||||||
|
|
||||||
|
=> setenv capsule_authentication_enabled 1
|
||||||
|
=> saveenv
|
||||||
|
|
||||||
|
2. Load the overlay dtb to memory and merge it with the base fdt
|
||||||
|
|
||||||
|
=> fatload virtio 0:1 <$fdtovaddr> EFI/<ov_dtb_file>
|
||||||
|
=> fdt addr $fdtcontroladdr
|
||||||
|
=> fdt resize <size_of_ov_dtb_file>
|
||||||
|
=> fdt apply <$fdtovaddr>
|
||||||
|
|
||||||
|
3. Set the following environment and UEFI boot variables
|
||||||
|
|
||||||
|
=> setenv -e -nv -bs -rt -v OsIndications =0x04
|
||||||
|
=> efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
|
||||||
|
=> efidebug boot next 0
|
||||||
|
=> saveenv
|
||||||
|
|
||||||
|
4. Finally, the capsule update can be initiated with the following
|
||||||
|
command
|
||||||
|
|
||||||
|
=> efidebug capsule disk-update
|
||||||
|
|
||||||
|
On subsequent reboot, the platform should boot the updated U-Boot binary.
|
|
@ -21,7 +21,7 @@ static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
|
||||||
static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
||||||
u64 offset, void *buf, long *len)
|
u64 offset, void *buf, long *len)
|
||||||
{
|
{
|
||||||
u64 off, lim, remaining;
|
u64 off, lim, remaining, lock_ofs, lock_len;
|
||||||
struct mtd_info *mtd = dfu->data.mtd.info;
|
struct mtd_info *mtd = dfu->data.mtd.info;
|
||||||
struct mtd_oob_ops io_op = {};
|
struct mtd_oob_ops io_op = {};
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -34,7 +34,7 @@ static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
off = dfu->data.mtd.start + offset + dfu->bad_skip;
|
off = lock_ofs = dfu->data.mtd.start + offset + dfu->bad_skip;
|
||||||
lim = dfu->data.mtd.start + dfu->data.mtd.size;
|
lim = dfu->data.mtd.start + dfu->data.mtd.size;
|
||||||
|
|
||||||
if (off >= lim) {
|
if (off >= lim) {
|
||||||
|
@ -56,12 +56,19 @@ static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
||||||
if (op == DFU_OP_WRITE) {
|
if (op == DFU_OP_WRITE) {
|
||||||
struct erase_info erase_op = {};
|
struct erase_info erase_op = {};
|
||||||
|
|
||||||
remaining = round_up(*len, mtd->erasesize);
|
remaining = lock_len = round_up(*len, mtd->erasesize);
|
||||||
erase_op.mtd = mtd;
|
erase_op.mtd = mtd;
|
||||||
erase_op.addr = off;
|
erase_op.addr = off;
|
||||||
erase_op.len = mtd->erasesize;
|
erase_op.len = mtd->erasesize;
|
||||||
erase_op.scrub = 0;
|
erase_op.scrub = 0;
|
||||||
|
|
||||||
|
debug("Unlocking the mtd device\n");
|
||||||
|
ret = mtd_unlock(mtd, lock_ofs, lock_len);
|
||||||
|
if (ret && ret != -EOPNOTSUPP) {
|
||||||
|
printf("MTD device unlock failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
if (erase_op.addr + remaining > lim) {
|
if (erase_op.addr + remaining > lim) {
|
||||||
printf("Limit reached 0x%llx while erasing at offset 0x%llx\n",
|
printf("Limit reached 0x%llx while erasing at offset 0x%llx\n",
|
||||||
|
@ -139,6 +146,13 @@ static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
||||||
io_op.len = mtd->writesize;
|
io_op.len = mtd->writesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op == DFU_OP_WRITE) {
|
||||||
|
/* Write done, lock again */
|
||||||
|
debug("Locking the mtd device\n");
|
||||||
|
ret = mtd_lock(mtd, lock_ofs, lock_len);
|
||||||
|
if (ret && ret != -EOPNOTSUPP)
|
||||||
|
printf("MTD device lock failed\n");
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
fs/fat/fat.c
23
fs/fat/fat.c
|
@ -810,7 +810,6 @@ static void fat_itr_child(fat_itr *itr, fat_itr *parent)
|
||||||
*/
|
*/
|
||||||
void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
|
void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
|
||||||
{
|
{
|
||||||
fsdata *mydata = itr->fsdata; /* for silly macros */
|
|
||||||
int ret;
|
int ret;
|
||||||
u32 sect;
|
u32 sect;
|
||||||
u32 read_size;
|
u32 read_size;
|
||||||
|
@ -838,8 +837,8 @@ void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
|
||||||
read_size = itr->fsdata->clust_size;
|
read_size = itr->fsdata->clust_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("FAT read(sect=%d), clust_size=%d, read_size=%u, DIRENTSPERBLOCK=%zd\n",
|
log_debug("FAT read(sect=%d), clust_size=%d, read_size=%u\n",
|
||||||
sect, itr->fsdata->clust_size, read_size, DIRENTSPERBLOCK);
|
sect, itr->fsdata->clust_size, read_size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: do_fat_read_at() had complicated logic to deal w/
|
* NOTE: do_fat_read_at() had complicated logic to deal w/
|
||||||
|
@ -1378,3 +1377,21 @@ void fat_closedir(struct fs_dir_stream *dirs)
|
||||||
void fat_close(void)
|
void fat_close(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fat_uuid(char *uuid_str)
|
||||||
|
{
|
||||||
|
boot_sector bs;
|
||||||
|
volume_info volinfo;
|
||||||
|
int fatsize;
|
||||||
|
int ret;
|
||||||
|
u8 *id;
|
||||||
|
|
||||||
|
ret = read_bootsectandvi(&bs, &volinfo, &fatsize);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
id = volinfo.volume_id;
|
||||||
|
sprintf(uuid_str, "%02X%02X-%02X%02X", id[3], id[2], id[1], id[0]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
2
fs/fs.c
2
fs/fs.c
|
@ -194,7 +194,7 @@ static struct fstype_info fstypes[] = {
|
||||||
.unlink = fs_unlink_unsupported,
|
.unlink = fs_unlink_unsupported,
|
||||||
.mkdir = fs_mkdir_unsupported,
|
.mkdir = fs_mkdir_unsupported,
|
||||||
#endif
|
#endif
|
||||||
.uuid = fs_uuid_unsupported,
|
.uuid = fat_uuid,
|
||||||
.opendir = fat_opendir,
|
.opendir = fat_opendir,
|
||||||
.readdir = fat_readdir,
|
.readdir = fat_readdir,
|
||||||
.closedir = fat_closedir,
|
.closedir = fat_closedir,
|
||||||
|
|
|
@ -1812,6 +1812,24 @@ struct efi_variable_authentication_2 {
|
||||||
struct win_certificate_uefi_guid auth_info;
|
struct win_certificate_uefi_guid auth_info;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* efi_firmware_image_authentication - Capsule authentication method
|
||||||
|
* descriptor
|
||||||
|
*
|
||||||
|
* This structure describes an authentication information for
|
||||||
|
* a capsule with IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED set
|
||||||
|
* and should be included as part of the capsule.
|
||||||
|
* Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
|
||||||
|
*
|
||||||
|
* @monotonic_count: Count to prevent replay
|
||||||
|
* @auth_info: Authentication info
|
||||||
|
*/
|
||||||
|
struct efi_firmware_image_authentication {
|
||||||
|
uint64_t monotonic_count;
|
||||||
|
struct win_certificate_uefi_guid auth_info;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_signature_data - A format of signature
|
* efi_signature_data - A format of signature
|
||||||
*
|
*
|
||||||
|
|
|
@ -304,15 +304,17 @@ enum efi_image_auth_status {
|
||||||
* @exit_status: exit status passed to Exit()
|
* @exit_status: exit status passed to Exit()
|
||||||
* @exit_data_size: exit data size passed to Exit()
|
* @exit_data_size: exit data size passed to Exit()
|
||||||
* @exit_data: exit data passed to Exit()
|
* @exit_data: exit data passed to Exit()
|
||||||
* @exit_jmp: long jump buffer for returning form started image
|
* @exit_jmp: long jump buffer for returning from started image
|
||||||
* @entry: entry address of the relocated image
|
* @entry: entry address of the relocated image
|
||||||
|
* @image_type: indicates if the image is an applicition or a driver
|
||||||
|
* @auth_status: indicates if the image is authenticated
|
||||||
*/
|
*/
|
||||||
struct efi_loaded_image_obj {
|
struct efi_loaded_image_obj {
|
||||||
struct efi_object header;
|
struct efi_object header;
|
||||||
efi_status_t exit_status;
|
efi_status_t *exit_status;
|
||||||
efi_uintn_t *exit_data_size;
|
efi_uintn_t *exit_data_size;
|
||||||
u16 **exit_data;
|
u16 **exit_data;
|
||||||
struct jmp_buf_data exit_jmp;
|
struct jmp_buf_data *exit_jmp;
|
||||||
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
|
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
|
||||||
struct efi_system_table *st);
|
struct efi_system_table *st);
|
||||||
u16 image_type;
|
u16 image_type;
|
||||||
|
@ -811,18 +813,27 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
|
||||||
int nocheck);
|
int nocheck);
|
||||||
|
|
||||||
void efi_sigstore_free(struct efi_signature_store *sigstore);
|
void efi_sigstore_free(struct efi_signature_store *sigstore);
|
||||||
|
struct efi_signature_store *efi_build_signature_store(void *sig_list,
|
||||||
|
efi_uintn_t size);
|
||||||
struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name);
|
struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name);
|
||||||
|
|
||||||
bool efi_secure_boot_enabled(void);
|
bool efi_secure_boot_enabled(void);
|
||||||
|
|
||||||
|
bool efi_capsule_auth_enabled(void);
|
||||||
|
|
||||||
bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
|
bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
|
||||||
WIN_CERTIFICATE **auth, size_t *auth_len);
|
WIN_CERTIFICATE **auth, size_t *auth_len);
|
||||||
|
|
||||||
|
struct pkcs7_message *efi_parse_pkcs7_header(const void *buf,
|
||||||
|
size_t buflen,
|
||||||
|
u8 **tmpbuf);
|
||||||
|
|
||||||
/* runtime implementation of memcpy() */
|
/* runtime implementation of memcpy() */
|
||||||
void efi_memcpy_runtime(void *dest, const void *src, size_t n);
|
void efi_memcpy_runtime(void *dest, const void *src, size_t n);
|
||||||
|
|
||||||
/* commonly used helper function */
|
/* commonly used helper function */
|
||||||
u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index);
|
u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name,
|
||||||
|
unsigned int index);
|
||||||
|
|
||||||
extern const struct efi_firmware_management_protocol efi_fmp_fit;
|
extern const struct efi_firmware_management_protocol efi_fmp_fit;
|
||||||
extern const struct efi_firmware_management_protocol efi_fmp_raw;
|
extern const struct efi_firmware_management_protocol efi_fmp_raw;
|
||||||
|
@ -838,6 +849,10 @@ efi_status_t EFIAPI efi_query_capsule_caps(
|
||||||
u64 *maximum_capsule_size,
|
u64 *maximum_capsule_size,
|
||||||
u32 *reset_type);
|
u32 *reset_type);
|
||||||
|
|
||||||
|
efi_status_t efi_capsule_authenticate(const void *capsule,
|
||||||
|
efi_uintn_t capsule_size,
|
||||||
|
void **image, efi_uintn_t *image_size);
|
||||||
|
|
||||||
#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
|
#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
|
||||||
|
|
||||||
/* Hook at initialization */
|
/* Hook at initialization */
|
||||||
|
|
|
@ -91,7 +91,7 @@ efi_status_t efi_query_variable_info_int(u32 attributes,
|
||||||
|
|
||||||
#define EFI_VAR_FILE_NAME "ubootefi.var"
|
#define EFI_VAR_FILE_NAME "ubootefi.var"
|
||||||
|
|
||||||
#define EFI_VAR_BUF_SIZE 0x4000
|
#define EFI_VAR_BUF_SIZE CONFIG_EFI_VAR_BUF_SIZE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This constant identifies the file format for storing UEFI variables in
|
* This constant identifies the file format for storing UEFI variables in
|
||||||
|
|
|
@ -22,7 +22,6 @@ struct disk_partition;
|
||||||
|
|
||||||
#define MAX_CLUSTSIZE CONFIG_FS_FAT_MAX_CLUSTSIZE
|
#define MAX_CLUSTSIZE CONFIG_FS_FAT_MAX_CLUSTSIZE
|
||||||
|
|
||||||
#define DIRENTSPERBLOCK (mydata->sect_size / sizeof(dir_entry))
|
|
||||||
#define DIRENTSPERCLUST ((mydata->clust_size * mydata->sect_size) / \
|
#define DIRENTSPERCLUST ((mydata->clust_size * mydata->sect_size) / \
|
||||||
sizeof(dir_entry))
|
sizeof(dir_entry))
|
||||||
|
|
||||||
|
@ -213,4 +212,16 @@ int fat_unlink(const char *filename);
|
||||||
int fat_mkdir(const char *dirname);
|
int fat_mkdir(const char *dirname);
|
||||||
void fat_close(void);
|
void fat_close(void);
|
||||||
void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes);
|
void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fat_uuid() - get FAT volume ID
|
||||||
|
*
|
||||||
|
* The FAT volume ID returned in @uuid_str as hexadecimal number in XXXX-XXXX
|
||||||
|
* format.
|
||||||
|
*
|
||||||
|
* @uuid_str: caller allocated buffer of at least 10 bytes for the volume ID
|
||||||
|
* Return: 0 on success
|
||||||
|
*/
|
||||||
|
int fat_uuid(char *uuid_str);
|
||||||
|
|
||||||
#endif /* _FAT_H_ */
|
#endif /* _FAT_H_ */
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FSP_TYPES_H__
|
#ifndef __SIGNATURES_H__
|
||||||
#define __FSP_TYPES_H__
|
#define __SIGNATURES_H__
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a 16-bit signature built from 2 ASCII characters.
|
* Returns a 16-bit signature built from 2 ASCII characters.
|
||||||
|
@ -59,4 +59,4 @@
|
||||||
#define SIGNATURE_64(A, B, C, D, E, F, G, H) \
|
#define SIGNATURE_64(A, B, C, D, E, F, G, H) \
|
||||||
(SIGNATURE_32(A, B, C, D) | ((u64)(SIGNATURE_32(E, F, G, H)) << 32))
|
(SIGNATURE_32(A, B, C, D) | ((u64)(SIGNATURE_32(E, F, G, H)) << 32))
|
||||||
|
|
||||||
#endif
|
#endif /* __SIGNATURES_H__ */
|
|
@ -50,8 +50,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||||
struct image_region regions[2];
|
struct image_region regions[2];
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* The digest was calculated already. */
|
/*
|
||||||
if (sig->digest)
|
* [RFC2315 9.3]
|
||||||
|
* If the authenticated attributes are present,
|
||||||
|
* the message-digest is calculated on the
|
||||||
|
* attributes present in the
|
||||||
|
* authenticatedAttributes field and not just
|
||||||
|
* the contents field
|
||||||
|
*/
|
||||||
|
if (!sinfo->authattrs && sig->digest)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!sinfo->sig->hash_algo)
|
if (!sinfo->sig->hash_algo)
|
||||||
|
@ -63,18 +70,26 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||||
else
|
else
|
||||||
return -ENOPKG;
|
return -ENOPKG;
|
||||||
|
|
||||||
sig->digest = calloc(1, sig->digest_size);
|
/*
|
||||||
if (!sig->digest) {
|
* Calculate the hash only if the data is present.
|
||||||
pr_warn("Sig %u: Out of memory\n", sinfo->index);
|
* In case of authenticated variable and capsule,
|
||||||
return -ENOMEM;
|
* the hash has already been calculated on the
|
||||||
|
* efi_image_regions and populated
|
||||||
|
*/
|
||||||
|
if (pkcs7->data) {
|
||||||
|
sig->digest = calloc(1, sig->digest_size);
|
||||||
|
if (!sig->digest) {
|
||||||
|
pr_warn("Sig %u: Out of memory\n", sinfo->index);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
regions[0].data = pkcs7->data;
|
||||||
|
regions[0].size = pkcs7->data_len;
|
||||||
|
|
||||||
|
/* Digest the message [RFC2315 9.3] */
|
||||||
|
hash_calculate(sinfo->sig->hash_algo, regions, 1, sig->digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
regions[0].data = pkcs7->data;
|
|
||||||
regions[0].size = pkcs7->data_len;
|
|
||||||
|
|
||||||
/* Digest the message [RFC2315 9.3] */
|
|
||||||
hash_calculate(sinfo->sig->hash_algo, regions, 1, sig->digest);
|
|
||||||
|
|
||||||
/* However, if there are authenticated attributes, there must be a
|
/* However, if there are authenticated attributes, there must be a
|
||||||
* message digest attribute amongst them which corresponds to the
|
* message digest attribute amongst them which corresponds to the
|
||||||
* digest we just calculated.
|
* digest we just calculated.
|
||||||
|
|
|
@ -77,6 +77,20 @@ config EFI_VAR_SEED_FILE
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
config EFI_VAR_BUF_SIZE
|
||||||
|
int "Memory size of the UEFI variable store"
|
||||||
|
default 16384
|
||||||
|
range 4096 2147483647
|
||||||
|
help
|
||||||
|
This defines the size in bytes of the memory area reserved for keeping
|
||||||
|
UEFI variables.
|
||||||
|
|
||||||
|
When using StandAloneMM (CONFIG_EFI_MM_COMM_TEE=y) this value should
|
||||||
|
match the value of PcdFlashNvStorageVariableSize used to compile the
|
||||||
|
StandAloneMM module.
|
||||||
|
|
||||||
|
Minimum 4096, default 16384.
|
||||||
|
|
||||||
config EFI_GET_TIME
|
config EFI_GET_TIME
|
||||||
bool "GetTime() runtime service"
|
bool "GetTime() runtime service"
|
||||||
depends on DM_RTC
|
depends on DM_RTC
|
||||||
|
@ -139,6 +153,23 @@ config EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||||
Select this option if you want to enable capsule-based
|
Select this option if you want to enable capsule-based
|
||||||
firmware update using Firmware Management Protocol.
|
firmware update using Firmware Management Protocol.
|
||||||
|
|
||||||
|
config EFI_CAPSULE_AUTHENTICATE
|
||||||
|
bool "Update Capsule authentication"
|
||||||
|
depends on EFI_CAPSULE_FIRMWARE
|
||||||
|
depends on EFI_CAPSULE_ON_DISK
|
||||||
|
depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||||
|
select SHA256
|
||||||
|
select RSA
|
||||||
|
select RSA_VERIFY
|
||||||
|
select RSA_VERIFY_WITH_PKEY
|
||||||
|
select X509_CERTIFICATE_PARSER
|
||||||
|
select PKCS7_MESSAGE_PARSER
|
||||||
|
select PKCS7_VERIFY
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Select this option if you want to enable capsule
|
||||||
|
authentication
|
||||||
|
|
||||||
config EFI_CAPSULE_FIRMWARE_FIT
|
config EFI_CAPSULE_FIRMWARE_FIT
|
||||||
bool "FMP driver for FIT image"
|
bool "FMP driver for FIT image"
|
||||||
depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||||
|
|
|
@ -275,7 +275,7 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
|
||||||
memcpy(*load_options, lo.optional_data, size);
|
memcpy(*load_options, lo.optional_data, size);
|
||||||
ret = efi_set_load_options(*handle, size, *load_options);
|
ret = efi_set_load_options(*handle, size, *load_options);
|
||||||
} else {
|
} else {
|
||||||
load_options = NULL;
|
*load_options = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
|
@ -247,8 +247,8 @@ static void efi_queue_event(struct efi_event *event)
|
||||||
}
|
}
|
||||||
if (event)
|
if (event)
|
||||||
list_add_tail(&event->queue_link, &efi_event_queue);
|
list_add_tail(&event->queue_link, &efi_event_queue);
|
||||||
|
efi_process_event_queue();
|
||||||
}
|
}
|
||||||
efi_process_event_queue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,8 +274,8 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl)
|
||||||
* efi_signal_event() - signal an EFI event
|
* efi_signal_event() - signal an EFI event
|
||||||
* @event: event to signal
|
* @event: event to signal
|
||||||
*
|
*
|
||||||
* This function signals an event. If the event belongs to an event group all
|
* This function signals an event. If the event belongs to an event group, all
|
||||||
* events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
|
* events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL,
|
||||||
* their notification function is queued.
|
* their notification function is queued.
|
||||||
*
|
*
|
||||||
* For the SignalEvent service see efi_signal_event_ext.
|
* For the SignalEvent service see efi_signal_event_ext.
|
||||||
|
@ -2161,7 +2161,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!efi_st_keep_devices) {
|
if (!efi_st_keep_devices) {
|
||||||
if IS_ENABLED(CONFIG_USB_DEVICE)
|
if (IS_ENABLED(CONFIG_USB_DEVICE))
|
||||||
udc_disconnect();
|
udc_disconnect();
|
||||||
board_quiesce_devices();
|
board_quiesce_devices();
|
||||||
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
|
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
|
||||||
|
@ -2978,6 +2978,8 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
|
||||||
efi_status_t ret;
|
efi_status_t ret;
|
||||||
void *info;
|
void *info;
|
||||||
efi_handle_t parent_image = current_image;
|
efi_handle_t parent_image = current_image;
|
||||||
|
efi_status_t exit_status;
|
||||||
|
struct jmp_buf_data exit_jmp;
|
||||||
|
|
||||||
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
|
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
|
||||||
|
|
||||||
|
@ -2999,9 +3001,11 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
|
||||||
|
|
||||||
image_obj->exit_data_size = exit_data_size;
|
image_obj->exit_data_size = exit_data_size;
|
||||||
image_obj->exit_data = exit_data;
|
image_obj->exit_data = exit_data;
|
||||||
|
image_obj->exit_status = &exit_status;
|
||||||
|
image_obj->exit_jmp = &exit_jmp;
|
||||||
|
|
||||||
/* call the image! */
|
/* call the image! */
|
||||||
if (setjmp(&image_obj->exit_jmp)) {
|
if (setjmp(&exit_jmp)) {
|
||||||
/*
|
/*
|
||||||
* We called the entry point of the child image with EFI_CALL
|
* We called the entry point of the child image with EFI_CALL
|
||||||
* in the lines below. The child image called the Exit() boot
|
* in the lines below. The child image called the Exit() boot
|
||||||
|
@ -3023,10 +3027,10 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
|
||||||
*/
|
*/
|
||||||
assert(__efi_entry_check());
|
assert(__efi_entry_check());
|
||||||
EFI_PRINT("%lu returned by started image\n",
|
EFI_PRINT("%lu returned by started image\n",
|
||||||
(unsigned long)((uintptr_t)image_obj->exit_status &
|
(unsigned long)((uintptr_t)exit_status &
|
||||||
~EFI_ERROR_MASK));
|
~EFI_ERROR_MASK));
|
||||||
current_image = parent_image;
|
current_image = parent_image;
|
||||||
return EFI_EXIT(image_obj->exit_status);
|
return EFI_EXIT(exit_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_image = image_handle;
|
current_image = image_handle;
|
||||||
|
@ -3209,6 +3213,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
|
||||||
struct efi_loaded_image *loaded_image_protocol;
|
struct efi_loaded_image *loaded_image_protocol;
|
||||||
struct efi_loaded_image_obj *image_obj =
|
struct efi_loaded_image_obj *image_obj =
|
||||||
(struct efi_loaded_image_obj *)image_handle;
|
(struct efi_loaded_image_obj *)image_handle;
|
||||||
|
struct jmp_buf_data *exit_jmp;
|
||||||
|
|
||||||
EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
|
EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
|
||||||
exit_data_size, exit_data);
|
exit_data_size, exit_data);
|
||||||
|
@ -3250,6 +3255,9 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
|
||||||
if (ret != EFI_SUCCESS)
|
if (ret != EFI_SUCCESS)
|
||||||
EFI_PRINT("%s: out of memory\n", __func__);
|
EFI_PRINT("%s: out of memory\n", __func__);
|
||||||
}
|
}
|
||||||
|
/* efi_delete_image() frees image_obj. Copy before the call. */
|
||||||
|
exit_jmp = image_obj->exit_jmp;
|
||||||
|
*image_obj->exit_status = exit_status;
|
||||||
if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
|
if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
|
||||||
exit_status != EFI_SUCCESS)
|
exit_status != EFI_SUCCESS)
|
||||||
efi_delete_image(image_obj, loaded_image_protocol);
|
efi_delete_image(image_obj, loaded_image_protocol);
|
||||||
|
@ -3263,8 +3271,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
|
||||||
*/
|
*/
|
||||||
efi_restore_gd();
|
efi_restore_gd();
|
||||||
|
|
||||||
image_obj->exit_status = exit_status;
|
longjmp(exit_jmp, 1);
|
||||||
longjmp(&image_obj->exit_jmp, 1);
|
|
||||||
|
|
||||||
panic("EFI application exited");
|
panic("EFI application exited");
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
#include <mapmem.h>
|
#include <mapmem.h>
|
||||||
#include <sort.h>
|
#include <sort.h>
|
||||||
|
|
||||||
|
#include <crypto/pkcs7.h>
|
||||||
|
#include <crypto/pkcs7_parser.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
|
const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
|
||||||
static const efi_guid_t efi_guid_firmware_management_capsule_id =
|
static const efi_guid_t efi_guid_firmware_management_capsule_id =
|
||||||
EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
|
||||||
|
@ -73,8 +77,8 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule,
|
||||||
struct efi_time time;
|
struct efi_time time;
|
||||||
efi_status_t ret;
|
efi_status_t ret;
|
||||||
|
|
||||||
efi_create_indexed_name(variable_name16, "Capsule", index);
|
efi_create_indexed_name(variable_name16, sizeof(variable_name16),
|
||||||
|
"Capsule", index);
|
||||||
result.variable_total_size = sizeof(result);
|
result.variable_total_size = sizeof(result);
|
||||||
result.capsule_guid = capsule->capsule_guid;
|
result.capsule_guid = capsule->capsule_guid;
|
||||||
ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
|
ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
|
||||||
|
@ -191,6 +195,124 @@ skip:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
|
||||||
|
|
||||||
|
const efi_guid_t efi_guid_capsule_root_cert_guid =
|
||||||
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
|
||||||
|
|
||||||
|
__weak int efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
|
||||||
|
{
|
||||||
|
/* The platform is supposed to provide
|
||||||
|
* a method for getting the public key
|
||||||
|
* stored in the form of efi signature
|
||||||
|
* list
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
|
||||||
|
void **image, efi_uintn_t *image_size)
|
||||||
|
{
|
||||||
|
u8 *buf;
|
||||||
|
int ret;
|
||||||
|
void *fdt_pkey, *pkey;
|
||||||
|
efi_uintn_t pkey_len;
|
||||||
|
uint64_t monotonic_count;
|
||||||
|
struct efi_signature_store *truststore;
|
||||||
|
struct pkcs7_message *capsule_sig;
|
||||||
|
struct efi_image_regions *regs;
|
||||||
|
struct efi_firmware_image_authentication *auth_hdr;
|
||||||
|
efi_status_t status;
|
||||||
|
|
||||||
|
status = EFI_SECURITY_VIOLATION;
|
||||||
|
capsule_sig = NULL;
|
||||||
|
truststore = NULL;
|
||||||
|
regs = NULL;
|
||||||
|
|
||||||
|
/* Sanity checks */
|
||||||
|
if (capsule == NULL || capsule_size == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
auth_hdr = (struct efi_firmware_image_authentication *)capsule;
|
||||||
|
if (capsule_size < sizeof(*auth_hdr))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (auth_hdr->auth_info.hdr.dwLength <=
|
||||||
|
offsetof(struct win_certificate_uefi_guid, cert_data))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (guidcmp(&auth_hdr->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*image = (uint8_t *)capsule + sizeof(auth_hdr->monotonic_count) +
|
||||||
|
auth_hdr->auth_info.hdr.dwLength;
|
||||||
|
*image_size = capsule_size - auth_hdr->auth_info.hdr.dwLength -
|
||||||
|
sizeof(auth_hdr->monotonic_count);
|
||||||
|
memcpy(&monotonic_count, &auth_hdr->monotonic_count,
|
||||||
|
sizeof(monotonic_count));
|
||||||
|
|
||||||
|
/* data to be digested */
|
||||||
|
regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 2, 1);
|
||||||
|
if (!regs)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
regs->max = 2;
|
||||||
|
efi_image_region_add(regs, (uint8_t *)*image,
|
||||||
|
(uint8_t *)*image + *image_size, 1);
|
||||||
|
|
||||||
|
efi_image_region_add(regs, (uint8_t *)&monotonic_count,
|
||||||
|
(uint8_t *)&monotonic_count + sizeof(monotonic_count),
|
||||||
|
1);
|
||||||
|
|
||||||
|
capsule_sig = efi_parse_pkcs7_header(auth_hdr->auth_info.cert_data,
|
||||||
|
auth_hdr->auth_info.hdr.dwLength
|
||||||
|
- sizeof(auth_hdr->auth_info),
|
||||||
|
&buf);
|
||||||
|
if (IS_ERR(capsule_sig)) {
|
||||||
|
debug("Parsing variable's pkcs7 header failed\n");
|
||||||
|
capsule_sig = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = efi_get_public_key_data(&fdt_pkey, &pkey_len);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pkey = malloc(pkey_len);
|
||||||
|
if (!pkey)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
memcpy(pkey, fdt_pkey, pkey_len);
|
||||||
|
truststore = efi_build_signature_store(pkey, pkey_len);
|
||||||
|
if (!truststore)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* verify signature */
|
||||||
|
if (efi_signature_verify(regs, capsule_sig, truststore, NULL)) {
|
||||||
|
debug("Verified\n");
|
||||||
|
} else {
|
||||||
|
debug("Verifying variable's signature failed\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = EFI_SUCCESS;
|
||||||
|
|
||||||
|
out:
|
||||||
|
efi_sigstore_free(truststore);
|
||||||
|
pkcs7_free_message(capsule_sig);
|
||||||
|
free(regs);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
|
||||||
|
void **image, efi_uintn_t *image_size)
|
||||||
|
{
|
||||||
|
return EFI_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_capsule_update_firmware - update firmware from capsule
|
* efi_capsule_update_firmware - update firmware from capsule
|
||||||
* @capsule_data: Capsule
|
* @capsule_data: Capsule
|
||||||
|
@ -896,7 +1018,8 @@ efi_status_t efi_launch_capsules(void)
|
||||||
free(files);
|
free(files);
|
||||||
|
|
||||||
/* CapsuleLast */
|
/* CapsuleLast */
|
||||||
efi_create_indexed_name(variable_name16, "Capsule", index - 1);
|
efi_create_indexed_name(variable_name16, sizeof(variable_name16),
|
||||||
|
"Capsule", index - 1);
|
||||||
efi_set_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
|
efi_set_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
|
||||||
EFI_VARIABLE_READ_ONLY |
|
EFI_VARIABLE_READ_ONLY |
|
||||||
EFI_VARIABLE_NON_VOLATILE |
|
EFI_VARIABLE_NON_VOLATILE |
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <env.h>
|
#include <env.h>
|
||||||
#include <stdio_dev.h>
|
#include <stdio_dev.h>
|
||||||
#include <video_console.h>
|
#include <video_console.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
#define EFI_COUT_MODE_2 2
|
#define EFI_COUT_MODE_2 2
|
||||||
#define EFI_MAX_COUT_MODE 3
|
#define EFI_MAX_COUT_MODE 3
|
||||||
|
@ -688,6 +689,17 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key)
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 0x1b:
|
case 0x1b:
|
||||||
|
/*
|
||||||
|
* If a second key is received within 10 ms, assume that we are
|
||||||
|
* dealing with an escape sequence. Otherwise consider this the
|
||||||
|
* escape key being hit. 10 ms is long enough to work fine at
|
||||||
|
* 1200 baud and above.
|
||||||
|
*/
|
||||||
|
udelay(10000);
|
||||||
|
if (!tstc()) {
|
||||||
|
pressed_key.scan_code = 23;
|
||||||
|
break;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Xterm Control Sequences
|
* Xterm Control Sequences
|
||||||
* https://www.xfree86.org/4.8.0/ctlseqs.html
|
* https://www.xfree86.org/4.8.0/ctlseqs.html
|
||||||
|
|
|
@ -11,8 +11,30 @@
|
||||||
#include <dfu.h>
|
#include <dfu.h>
|
||||||
#include <efi_loader.h>
|
#include <efi_loader.h>
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
|
#include <signatures.h>
|
||||||
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
#define FMP_PAYLOAD_HDR_SIGNATURE SIGNATURE_32('M', 'S', 'S', '1')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct fmp_payload_header - EDK2 header for the FMP payload
|
||||||
|
*
|
||||||
|
* This structure describes the header which is preprended to the
|
||||||
|
* FMP payload by the edk2 capsule generation scripts.
|
||||||
|
*
|
||||||
|
* @signature: Header signature used to identify the header
|
||||||
|
* @header_size: Size of the structure
|
||||||
|
* @fw_version: Firmware versions used
|
||||||
|
* @lowest_supported_version: Lowest supported version
|
||||||
|
*/
|
||||||
|
struct fmp_payload_header {
|
||||||
|
u32 signature;
|
||||||
|
u32 header_size;
|
||||||
|
u32 fw_version;
|
||||||
|
u32 lowest_supported_version;
|
||||||
|
};
|
||||||
|
|
||||||
/* Place holder; not supported */
|
/* Place holder; not supported */
|
||||||
static
|
static
|
||||||
efi_status_t EFIAPI efi_firmware_get_image_unsupported(
|
efi_status_t EFIAPI efi_firmware_get_image_unsupported(
|
||||||
|
@ -162,9 +184,16 @@ static efi_status_t efi_get_dfu_info(
|
||||||
image_info[i].version_name = NULL; /* not supported */
|
image_info[i].version_name = NULL; /* not supported */
|
||||||
image_info[i].size = 0;
|
image_info[i].size = 0;
|
||||||
image_info[i].attributes_supported =
|
image_info[i].attributes_supported =
|
||||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
|
||||||
|
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
|
||||||
image_info[i].attributes_setting =
|
image_info[i].attributes_setting =
|
||||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
||||||
|
|
||||||
|
/* Check if the capsule authentication is enabled */
|
||||||
|
if (env_get("capsule_authentication_enabled"))
|
||||||
|
image_info[0].attributes_setting |=
|
||||||
|
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
|
||||||
|
|
||||||
image_info[i].lowest_supported_image_version = 0;
|
image_info[i].lowest_supported_image_version = 0;
|
||||||
image_info[i].last_attempt_version = 0;
|
image_info[i].last_attempt_version = 0;
|
||||||
image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
|
image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
|
||||||
|
@ -379,12 +408,58 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
|
||||||
efi_status_t (*progress)(efi_uintn_t completion),
|
efi_status_t (*progress)(efi_uintn_t completion),
|
||||||
u16 **abort_reason)
|
u16 **abort_reason)
|
||||||
{
|
{
|
||||||
|
u32 fmp_hdr_signature;
|
||||||
|
struct fmp_payload_header *header;
|
||||||
|
void *capsule_payload;
|
||||||
|
efi_status_t status;
|
||||||
|
efi_uintn_t capsule_payload_size;
|
||||||
|
|
||||||
EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
|
EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
|
||||||
image_size, vendor_code, progress, abort_reason);
|
image_size, vendor_code, progress, abort_reason);
|
||||||
|
|
||||||
if (!image)
|
if (!image)
|
||||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||||
|
|
||||||
|
/* Authenticate the capsule if authentication enabled */
|
||||||
|
if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) &&
|
||||||
|
env_get("capsule_authentication_enabled")) {
|
||||||
|
capsule_payload = NULL;
|
||||||
|
capsule_payload_size = 0;
|
||||||
|
status = efi_capsule_authenticate(image, image_size,
|
||||||
|
&capsule_payload,
|
||||||
|
&capsule_payload_size);
|
||||||
|
|
||||||
|
if (status == EFI_SECURITY_VIOLATION) {
|
||||||
|
printf("Capsule authentication check failed. Aborting update\n");
|
||||||
|
return EFI_EXIT(status);
|
||||||
|
} else if (status != EFI_SUCCESS) {
|
||||||
|
return EFI_EXIT(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Capsule authentication successfull\n");
|
||||||
|
image = capsule_payload;
|
||||||
|
image_size = capsule_payload_size;
|
||||||
|
} else {
|
||||||
|
debug("Capsule authentication disabled. ");
|
||||||
|
debug("Updating capsule without authenticating.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
|
||||||
|
header = (void *)image;
|
||||||
|
|
||||||
|
if (!memcmp(&header->signature, &fmp_hdr_signature,
|
||||||
|
sizeof(fmp_hdr_signature))) {
|
||||||
|
/*
|
||||||
|
* When building the capsule with the scripts in
|
||||||
|
* edk2, a FMP header is inserted above the capsule
|
||||||
|
* payload. Compensate for this header to get the
|
||||||
|
* actual payload that is to be updated.
|
||||||
|
*/
|
||||||
|
image += header->header_size;
|
||||||
|
image_size -= header->header_size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
|
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
|
||||||
NULL, NULL))
|
NULL, NULL))
|
||||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||||
|
|
|
@ -4,13 +4,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <env.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <mapmem.h>
|
|
||||||
#include <dm.h>
|
|
||||||
#include <fs.h>
|
|
||||||
#include <efi_loader.h>
|
#include <efi_loader.h>
|
||||||
#include <efi_load_initrd.h>
|
#include <efi_load_initrd.h>
|
||||||
|
#include <fs.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <mapmem.h>
|
||||||
|
|
||||||
static efi_status_t EFIAPI
|
static efi_status_t EFIAPI
|
||||||
efi_load_file2_initrd(struct efi_load_file_protocol *this,
|
efi_load_file2_initrd(struct efi_load_file_protocol *this,
|
||||||
|
|
|
@ -257,11 +257,6 @@ efi_status_t efi_init_obj_list(void)
|
||||||
if (ret != EFI_SUCCESS)
|
if (ret != EFI_SUCCESS)
|
||||||
goto out;
|
goto out;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_EFI_LOAD_FILE2_INITRD
|
|
||||||
ret = efi_initrd_register();
|
|
||||||
if (ret != EFI_SUCCESS)
|
|
||||||
goto out;
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET
|
#ifdef CONFIG_NET
|
||||||
ret = efi_net_register();
|
ret = efi_net_register();
|
||||||
if (ret != EFI_SUCCESS)
|
if (ret != EFI_SUCCESS)
|
||||||
|
|
|
@ -26,7 +26,92 @@ const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
|
||||||
const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
|
const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
|
||||||
const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
|
const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
|
||||||
|
|
||||||
#ifdef CONFIG_EFI_SECURE_BOOT
|
#if defined(CONFIG_EFI_SECURE_BOOT) || defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
|
||||||
|
static u8 pkcs7_hdr[] = {
|
||||||
|
/* SEQUENCE */
|
||||||
|
0x30, 0x82, 0x05, 0xc7,
|
||||||
|
/* OID: pkcs7-signedData */
|
||||||
|
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
|
||||||
|
/* Context Structured? */
|
||||||
|
0xa0, 0x82, 0x05, 0xb8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* efi_parse_pkcs7_header - parse a signature in payload
|
||||||
|
* @buf: Pointer to payload's value
|
||||||
|
* @buflen: Length of @buf
|
||||||
|
* @tmpbuf: Pointer to temporary buffer
|
||||||
|
*
|
||||||
|
* Parse a signature embedded in payload's value and instantiate
|
||||||
|
* a pkcs7_message structure. Since pkcs7_parse_message() accepts only
|
||||||
|
* pkcs7's signedData, some header needed be prepended for correctly
|
||||||
|
* parsing authentication data
|
||||||
|
* A temporary buffer will be allocated if needed, and it should be
|
||||||
|
* kept valid during the authentication because some data in the buffer
|
||||||
|
* will be referenced by efi_signature_verify().
|
||||||
|
*
|
||||||
|
* Return: Pointer to pkcs7_message structure on success, NULL on error
|
||||||
|
*/
|
||||||
|
struct pkcs7_message *efi_parse_pkcs7_header(const void *buf,
|
||||||
|
size_t buflen,
|
||||||
|
u8 **tmpbuf)
|
||||||
|
{
|
||||||
|
u8 *ebuf;
|
||||||
|
size_t ebuflen, len;
|
||||||
|
struct pkcs7_message *msg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the best assumption to check if the binary is
|
||||||
|
* already in a form of pkcs7's signedData.
|
||||||
|
*/
|
||||||
|
if (buflen > sizeof(pkcs7_hdr) &&
|
||||||
|
!memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
|
||||||
|
msg = pkcs7_parse_message(buf, buflen);
|
||||||
|
if (IS_ERR(msg))
|
||||||
|
return NULL;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, we should add a dummy prefix sequence for pkcs7
|
||||||
|
* message parser to be able to process.
|
||||||
|
* NOTE: EDK2 also uses similar hack in WrapPkcs7Data()
|
||||||
|
* in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
|
||||||
|
* TODO:
|
||||||
|
* The header should be composed in a more refined manner.
|
||||||
|
*/
|
||||||
|
EFI_PRINT("Makeshift prefix added to authentication data\n");
|
||||||
|
ebuflen = sizeof(pkcs7_hdr) + buflen;
|
||||||
|
if (ebuflen <= 0x7f) {
|
||||||
|
EFI_PRINT("Data is too short\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ebuf = malloc(ebuflen);
|
||||||
|
if (!ebuf) {
|
||||||
|
EFI_PRINT("Out of memory\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr));
|
||||||
|
memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen);
|
||||||
|
len = ebuflen - 4;
|
||||||
|
ebuf[2] = (len >> 8) & 0xff;
|
||||||
|
ebuf[3] = len & 0xff;
|
||||||
|
len = ebuflen - 0x13;
|
||||||
|
ebuf[0x11] = (len >> 8) & 0xff;
|
||||||
|
ebuf[0x12] = len & 0xff;
|
||||||
|
|
||||||
|
msg = pkcs7_parse_message(ebuf, ebuflen);
|
||||||
|
|
||||||
|
if (IS_ERR(msg)) {
|
||||||
|
free(ebuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmpbuf = ebuf;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_hash_regions - calculate a hash value
|
* efi_hash_regions - calculate a hash value
|
||||||
|
@ -651,6 +736,63 @@ err:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* efi_sigstore_parse_sigdb - parse the signature list and populate
|
||||||
|
* the signature store
|
||||||
|
*
|
||||||
|
* @sig_list: Pointer to the signature list
|
||||||
|
* @size: Size of the signature list
|
||||||
|
*
|
||||||
|
* Parse the efi signature list and instantiate a signature store
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* Return: Pointer to signature store on success, NULL on error
|
||||||
|
*/
|
||||||
|
struct efi_signature_store *efi_build_signature_store(void *sig_list,
|
||||||
|
efi_uintn_t size)
|
||||||
|
{
|
||||||
|
struct efi_signature_list *esl;
|
||||||
|
struct efi_signature_store *sigstore = NULL, *siglist;
|
||||||
|
|
||||||
|
esl = sig_list;
|
||||||
|
while (size > 0) {
|
||||||
|
/* List must exist if there is remaining data. */
|
||||||
|
if (size < sizeof(*esl)) {
|
||||||
|
EFI_PRINT("Signature list in wrong format\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < esl->signature_list_size) {
|
||||||
|
EFI_PRINT("Signature list in wrong format\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a single siglist. */
|
||||||
|
siglist = efi_sigstore_parse_siglist(esl);
|
||||||
|
if (!siglist) {
|
||||||
|
EFI_PRINT("Parsing of signature list of failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append siglist */
|
||||||
|
siglist->next = sigstore;
|
||||||
|
sigstore = siglist;
|
||||||
|
|
||||||
|
/* Next */
|
||||||
|
size -= esl->signature_list_size;
|
||||||
|
esl = (void *)esl + esl->signature_list_size;
|
||||||
|
}
|
||||||
|
free(sig_list);
|
||||||
|
|
||||||
|
return sigstore;
|
||||||
|
|
||||||
|
err:
|
||||||
|
efi_sigstore_free(sigstore);
|
||||||
|
free(sig_list);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_sigstore_parse_sigdb - parse a signature database variable
|
* efi_sigstore_parse_sigdb - parse a signature database variable
|
||||||
* @name: Variable's name
|
* @name: Variable's name
|
||||||
|
@ -662,8 +804,7 @@ err:
|
||||||
*/
|
*/
|
||||||
struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
|
struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
|
||||||
{
|
{
|
||||||
struct efi_signature_store *sigstore = NULL, *siglist;
|
struct efi_signature_store *sigstore = NULL;
|
||||||
struct efi_signature_list *esl;
|
|
||||||
const efi_guid_t *vendor;
|
const efi_guid_t *vendor;
|
||||||
void *db;
|
void *db;
|
||||||
efi_uintn_t db_size;
|
efi_uintn_t db_size;
|
||||||
|
@ -699,47 +840,10 @@ struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
|
||||||
ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));
|
ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));
|
||||||
if (ret != EFI_SUCCESS) {
|
if (ret != EFI_SUCCESS) {
|
||||||
EFI_PRINT("Getting variable, %ls, failed\n", name);
|
EFI_PRINT("Getting variable, %ls, failed\n", name);
|
||||||
goto err;
|
free(db);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse siglist list */
|
return efi_build_signature_store(db, db_size);
|
||||||
esl = db;
|
|
||||||
while (db_size > 0) {
|
|
||||||
/* List must exist if there is remaining data. */
|
|
||||||
if (db_size < sizeof(*esl)) {
|
|
||||||
EFI_PRINT("variable, %ls, in wrong format\n", name);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db_size < esl->signature_list_size) {
|
|
||||||
EFI_PRINT("variable, %ls, in wrong format\n", name);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse a single siglist. */
|
|
||||||
siglist = efi_sigstore_parse_siglist(esl);
|
|
||||||
if (!siglist) {
|
|
||||||
EFI_PRINT("Parsing signature list of %ls failed\n",
|
|
||||||
name);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Append siglist */
|
|
||||||
siglist->next = sigstore;
|
|
||||||
sigstore = siglist;
|
|
||||||
|
|
||||||
/* Next */
|
|
||||||
db_size -= esl->signature_list_size;
|
|
||||||
esl = (void *)esl + esl->signature_list_size;
|
|
||||||
}
|
|
||||||
free(db);
|
|
||||||
|
|
||||||
return sigstore;
|
|
||||||
|
|
||||||
err:
|
|
||||||
efi_sigstore_free(sigstore);
|
|
||||||
free(db);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_EFI_SECURE_BOOT */
|
#endif /* CONFIG_EFI_SECURE_BOOT || CONFIG_EFI_CAPSULE_AUTHENTICATE */
|
||||||
|
|
|
@ -23,13 +23,19 @@
|
||||||
* Return: A pointer to the next position after the created string
|
* Return: A pointer to the next position after the created string
|
||||||
* in @buffer, or NULL otherwise
|
* in @buffer, or NULL otherwise
|
||||||
*/
|
*/
|
||||||
u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index)
|
u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name,
|
||||||
|
unsigned int index)
|
||||||
{
|
{
|
||||||
u16 *p = buffer;
|
u16 *p = buffer;
|
||||||
char index_buf[5];
|
char index_buf[5];
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
size = (utf8_utf16_strlen(name) * sizeof(u16) +
|
||||||
|
sizeof(index_buf) * sizeof(u16));
|
||||||
|
if (buffer_size < size)
|
||||||
|
return NULL;
|
||||||
utf8_utf16_strcpy(&p, name);
|
utf8_utf16_strcpy(&p, name);
|
||||||
sprintf(index_buf, "%04X", index);
|
snprintf(index_buf, sizeof(index_buf), "%04X", index);
|
||||||
utf8_utf16_strcpy(&p, index_buf);
|
utf8_utf16_strcpy(&p, index_buf);
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
|
|
|
@ -24,91 +24,6 @@
|
||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
|
|
||||||
#ifdef CONFIG_EFI_SECURE_BOOT
|
#ifdef CONFIG_EFI_SECURE_BOOT
|
||||||
static u8 pkcs7_hdr[] = {
|
|
||||||
/* SEQUENCE */
|
|
||||||
0x30, 0x82, 0x05, 0xc7,
|
|
||||||
/* OID: pkcs7-signedData */
|
|
||||||
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
|
|
||||||
/* Context Structured? */
|
|
||||||
0xa0, 0x82, 0x05, 0xb8,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* efi_variable_parse_signature - parse a signature in variable
|
|
||||||
* @buf: Pointer to variable's value
|
|
||||||
* @buflen: Length of @buf
|
|
||||||
* @tmpbuf: Pointer to temporary buffer
|
|
||||||
*
|
|
||||||
* Parse a signature embedded in variable's value and instantiate
|
|
||||||
* a pkcs7_message structure. Since pkcs7_parse_message() accepts only
|
|
||||||
* pkcs7's signedData, some header needed be prepended for correctly
|
|
||||||
* parsing authentication data, particularly for variable's.
|
|
||||||
* A temporary buffer will be allocated if needed, and it should be
|
|
||||||
* kept valid during the authentication because some data in the buffer
|
|
||||||
* will be referenced by efi_signature_verify().
|
|
||||||
*
|
|
||||||
* Return: Pointer to pkcs7_message structure on success, NULL on error
|
|
||||||
*/
|
|
||||||
static struct pkcs7_message *efi_variable_parse_signature(const void *buf,
|
|
||||||
size_t buflen,
|
|
||||||
u8 **tmpbuf)
|
|
||||||
{
|
|
||||||
u8 *ebuf;
|
|
||||||
size_t ebuflen, len;
|
|
||||||
struct pkcs7_message *msg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is the best assumption to check if the binary is
|
|
||||||
* already in a form of pkcs7's signedData.
|
|
||||||
*/
|
|
||||||
if (buflen > sizeof(pkcs7_hdr) &&
|
|
||||||
!memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
|
|
||||||
msg = pkcs7_parse_message(buf, buflen);
|
|
||||||
if (IS_ERR(msg))
|
|
||||||
return NULL;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Otherwise, we should add a dummy prefix sequence for pkcs7
|
|
||||||
* message parser to be able to process.
|
|
||||||
* NOTE: EDK2 also uses similar hack in WrapPkcs7Data()
|
|
||||||
* in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
|
|
||||||
* TODO:
|
|
||||||
* The header should be composed in a more refined manner.
|
|
||||||
*/
|
|
||||||
EFI_PRINT("Makeshift prefix added to authentication data\n");
|
|
||||||
ebuflen = sizeof(pkcs7_hdr) + buflen;
|
|
||||||
if (ebuflen <= 0x7f) {
|
|
||||||
EFI_PRINT("Data is too short\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ebuf = malloc(ebuflen);
|
|
||||||
if (!ebuf) {
|
|
||||||
EFI_PRINT("Out of memory\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr));
|
|
||||||
memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen);
|
|
||||||
len = ebuflen - 4;
|
|
||||||
ebuf[2] = (len >> 8) & 0xff;
|
|
||||||
ebuf[3] = len & 0xff;
|
|
||||||
len = ebuflen - 0x13;
|
|
||||||
ebuf[0x11] = (len >> 8) & 0xff;
|
|
||||||
ebuf[0x12] = len & 0xff;
|
|
||||||
|
|
||||||
msg = pkcs7_parse_message(ebuf, ebuflen);
|
|
||||||
|
|
||||||
if (IS_ERR(msg)) {
|
|
||||||
free(ebuf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*tmpbuf = ebuf;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_variable_authenticate - authenticate a variable
|
* efi_variable_authenticate - authenticate a variable
|
||||||
|
@ -215,10 +130,10 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* ebuf should be kept valid during the authentication */
|
/* ebuf should be kept valid during the authentication */
|
||||||
var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
|
var_sig = efi_parse_pkcs7_header(auth->auth_info.cert_data,
|
||||||
auth->auth_info.hdr.dwLength
|
auth->auth_info.hdr.dwLength
|
||||||
- sizeof(auth->auth_info),
|
- sizeof(auth->auth_info),
|
||||||
&ebuf);
|
&ebuf);
|
||||||
if (!var_sig) {
|
if (!var_sig) {
|
||||||
EFI_PRINT("Parsing variable's signature failed\n");
|
EFI_PRINT("Parsing variable's signature failed\n");
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
@ -36,20 +36,29 @@ static int get_connection(struct mm_connection *conn)
|
||||||
static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
|
static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
|
||||||
struct udevice *tee = NULL;
|
struct udevice *tee = NULL;
|
||||||
struct tee_open_session_arg arg;
|
struct tee_open_session_arg arg;
|
||||||
int rc;
|
int rc = -ENODEV;
|
||||||
|
|
||||||
tee = tee_find_device(tee, NULL, NULL, NULL);
|
tee = tee_find_device(tee, NULL, NULL, NULL);
|
||||||
if (!tee)
|
if (!tee)
|
||||||
return -ENODEV;
|
goto out;
|
||||||
|
|
||||||
memset(&arg, 0, sizeof(arg));
|
memset(&arg, 0, sizeof(arg));
|
||||||
tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
|
tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
|
||||||
rc = tee_open_session(tee, &arg, 0, NULL);
|
rc = tee_open_session(tee, &arg, 0, NULL);
|
||||||
if (!rc) {
|
if (rc)
|
||||||
conn->tee = tee;
|
goto out;
|
||||||
conn->session = arg.session;
|
|
||||||
|
/* Check the internal OP-TEE result */
|
||||||
|
if (arg.ret != TEE_SUCCESS) {
|
||||||
|
rc = -EIO;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn->tee = tee;
|
||||||
|
conn->session = arg.session;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +97,7 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
|
||||||
|
|
||||||
if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
|
if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
|
||||||
log_err("Unable to register shared memory\n");
|
log_err("Unable to register shared memory\n");
|
||||||
|
tee_close_session(conn.tee, conn.session);
|
||||||
return EFI_UNSUPPORTED;
|
return EFI_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -603,7 +603,7 @@ static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts)
|
||||||
u16 *pos;
|
u16 *pos;
|
||||||
|
|
||||||
memset(buf, 0xeb, sizeof(buf));
|
memset(buf, 0xeb, sizeof(buf));
|
||||||
pos = efi_create_indexed_name(buf, "Capsule", 0x0af9);
|
pos = efi_create_indexed_name(buf, sizeof(buf), "Capsule", 0x0af9);
|
||||||
|
|
||||||
ut_asserteq_mem(expected, buf, sizeof(expected));
|
ut_asserteq_mem(expected, buf, sizeof(expected));
|
||||||
ut_asserteq(pos - buf, u16_strnlen(buf, SIZE_MAX));
|
ut_asserteq(pos - buf, u16_strnlen(buf, SIZE_MAX));
|
||||||
|
|
|
@ -218,6 +218,7 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
|
||||||
hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
|
hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
|
||||||
HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
|
HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
|
||||||
|
|
||||||
|
mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
|
||||||
hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
|
hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
|
||||||
|
|
||||||
# We build some files with extra pedantic flags to try to minimize things
|
# We build some files with extra pedantic flags to try to minimize things
|
||||||
|
|
|
@ -4,16 +4,22 @@
|
||||||
* Author: AKASHI Takahiro
|
* Author: AKASHI Takahiro
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "fdt_host.h"
|
||||||
|
|
||||||
typedef __u8 u8;
|
typedef __u8 u8;
|
||||||
typedef __u16 u16;
|
typedef __u16 u16;
|
||||||
typedef __u32 u32;
|
typedef __u32 u32;
|
||||||
|
@ -23,6 +29,9 @@ typedef __s32 s32;
|
||||||
|
|
||||||
#define aligned_u64 __aligned_u64
|
#define aligned_u64 __aligned_u64
|
||||||
|
|
||||||
|
#define SIGNATURE_NODENAME "signature"
|
||||||
|
#define OVERLAY_NODENAME "__overlay__"
|
||||||
|
|
||||||
#ifndef __packed
|
#ifndef __packed
|
||||||
#define __packed __attribute__((packed))
|
#define __packed __attribute__((packed))
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,6 +52,9 @@ static struct option options[] = {
|
||||||
{"raw", required_argument, NULL, 'r'},
|
{"raw", required_argument, NULL, 'r'},
|
||||||
{"index", required_argument, NULL, 'i'},
|
{"index", required_argument, NULL, 'i'},
|
||||||
{"instance", required_argument, NULL, 'I'},
|
{"instance", required_argument, NULL, 'I'},
|
||||||
|
{"dtb", required_argument, NULL, 'D'},
|
||||||
|
{"public key", required_argument, NULL, 'K'},
|
||||||
|
{"overlay", no_argument, NULL, 'O'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
@ -51,14 +63,183 @@ static void print_usage(void)
|
||||||
{
|
{
|
||||||
printf("Usage: %s [options] <output file>\n"
|
printf("Usage: %s [options] <output file>\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
"\t--fit <fit image> new FIT image file\n"
|
|
||||||
"\t--raw <raw image> new raw image file\n"
|
"\t--fit <fit image> new FIT image file\n"
|
||||||
"\t--index <index> update image index\n"
|
"\t--raw <raw image> new raw image file\n"
|
||||||
"\t--instance <instance> update hardware instance\n"
|
"\t--index <index> update image index\n"
|
||||||
"\t--help print a help message\n",
|
"\t--instance <instance> update hardware instance\n"
|
||||||
|
"\t--public-key <key file> public key esl file\n"
|
||||||
|
"\t--dtb <dtb file> dtb file\n"
|
||||||
|
"\t--overlay the dtb file is an overlay\n"
|
||||||
|
"\t--help print a help message\n",
|
||||||
tool_name);
|
tool_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
|
||||||
|
bool overlay)
|
||||||
|
{
|
||||||
|
int parent;
|
||||||
|
int ov_node;
|
||||||
|
int frag_node;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (overlay) {
|
||||||
|
/*
|
||||||
|
* The signature would be stored in the
|
||||||
|
* first fragment node of the overlay
|
||||||
|
*/
|
||||||
|
frag_node = fdt_first_subnode(dptr, 0);
|
||||||
|
if (frag_node == -FDT_ERR_NOTFOUND) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Couldn't find the fragment node: %s\n",
|
||||||
|
fdt_strerror(frag_node));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
|
||||||
|
if (ov_node == -FDT_ERR_NOTFOUND) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Couldn't find the __overlay__ node: %s\n",
|
||||||
|
fdt_strerror(ov_node));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ov_node = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
|
||||||
|
if (parent == -FDT_ERR_NOTFOUND) {
|
||||||
|
parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
|
||||||
|
if (parent < 0) {
|
||||||
|
ret = parent;
|
||||||
|
if (ret != -FDT_ERR_NOSPACE) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Couldn't create signature node: %s\n",
|
||||||
|
fdt_strerror(parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Write the key to the FDT node */
|
||||||
|
ret = fdt_setprop(dptr, parent, "capsule-key",
|
||||||
|
sptr, key_size);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (ret)
|
||||||
|
ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_public_key(const char *pkey_file, const char *dtb_file,
|
||||||
|
bool overlay)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int srcfd = 0;
|
||||||
|
int destfd = 0;
|
||||||
|
void *sptr = NULL;
|
||||||
|
void *dptr = NULL;
|
||||||
|
off_t src_size;
|
||||||
|
struct stat pub_key;
|
||||||
|
struct stat dtb;
|
||||||
|
|
||||||
|
/* Find out the size of the public key */
|
||||||
|
srcfd = open(pkey_file, O_RDONLY);
|
||||||
|
if (srcfd == -1) {
|
||||||
|
fprintf(stderr, "%s: Can't open %s: %s\n",
|
||||||
|
__func__, pkey_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fstat(srcfd, &pub_key);
|
||||||
|
if (ret == -1) {
|
||||||
|
fprintf(stderr, "%s: Can't stat %s: %s\n",
|
||||||
|
__func__, pkey_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
src_size = pub_key.st_size;
|
||||||
|
|
||||||
|
/* mmap the public key esl file */
|
||||||
|
sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
|
||||||
|
if ((sptr == MAP_FAILED) || (errno != 0)) {
|
||||||
|
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
|
||||||
|
__func__, pkey_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the dest FDT */
|
||||||
|
destfd = open(dtb_file, O_RDWR);
|
||||||
|
if (destfd == -1) {
|
||||||
|
fprintf(stderr, "%s: Can't open %s: %s\n",
|
||||||
|
__func__, dtb_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fstat(destfd, &dtb);
|
||||||
|
if (ret == -1) {
|
||||||
|
fprintf(stderr, "%s: Can't stat %s: %s\n",
|
||||||
|
__func__, dtb_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtb.st_size += src_size + 0x30;
|
||||||
|
if (ftruncate(destfd, dtb.st_size)) {
|
||||||
|
fprintf(stderr, "%s: Can't expand %s: %s\n",
|
||||||
|
__func__, dtb_file, strerror(errno));
|
||||||
|
goto err;;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
/* mmap the dtb file */
|
||||||
|
dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||||
|
destfd, 0);
|
||||||
|
if ((dptr == MAP_FAILED) || (errno != 0)) {
|
||||||
|
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
|
||||||
|
__func__, dtb_file, strerror(errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdt_check_header(dptr)) {
|
||||||
|
fprintf(stderr, "%s: Invalid FDT header\n", __func__);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fdt_open_into(dptr, dptr, dtb.st_size);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%s: Cannot expand FDT: %s\n",
|
||||||
|
__func__, fdt_strerror(ret));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the esl file to the expanded FDT */
|
||||||
|
ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%s: Unable to add public key to the FDT\n",
|
||||||
|
__func__);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (sptr)
|
||||||
|
munmap(sptr, src_size);
|
||||||
|
|
||||||
|
if (dptr)
|
||||||
|
munmap(dptr, dtb.st_size);
|
||||||
|
|
||||||
|
if (srcfd >= 0)
|
||||||
|
close(srcfd);
|
||||||
|
|
||||||
|
if (destfd >= 0)
|
||||||
|
close(destfd);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
|
static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
|
||||||
unsigned long index, unsigned long instance)
|
unsigned long index, unsigned long instance)
|
||||||
{
|
{
|
||||||
|
@ -173,16 +354,22 @@ err_1:
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *file;
|
char *file;
|
||||||
|
char *pkey_file;
|
||||||
|
char *dtb_file;
|
||||||
efi_guid_t *guid;
|
efi_guid_t *guid;
|
||||||
unsigned long index, instance;
|
unsigned long index, instance;
|
||||||
int c, idx;
|
int c, idx;
|
||||||
|
int ret;
|
||||||
|
bool overlay = false;
|
||||||
|
|
||||||
file = NULL;
|
file = NULL;
|
||||||
|
pkey_file = NULL;
|
||||||
|
dtb_file = NULL;
|
||||||
guid = NULL;
|
guid = NULL;
|
||||||
index = 0;
|
index = 0;
|
||||||
instance = 0;
|
instance = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
|
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -209,22 +396,44 @@ int main(int argc, char **argv)
|
||||||
case 'I':
|
case 'I':
|
||||||
instance = strtoul(optarg, NULL, 0);
|
instance = strtoul(optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
|
case 'K':
|
||||||
|
if (pkey_file) {
|
||||||
|
printf("Public Key already specified\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pkey_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (dtb_file) {
|
||||||
|
printf("DTB file already specified\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dtb_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
overlay = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
print_usage();
|
print_usage();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* need a output file */
|
/* need a fit image file or raw image file */
|
||||||
if (argc != optind + 1) {
|
if (!file && !pkey_file && !dtb_file) {
|
||||||
|
printf("%s: %d\n", __func__, __LINE__);
|
||||||
print_usage();
|
print_usage();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* need a fit image file or raw image file */
|
if (pkey_file && dtb_file) {
|
||||||
if (!file) {
|
ret = add_public_key(pkey_file, dtb_file, overlay);
|
||||||
print_usage();
|
if (ret == -1) {
|
||||||
return -1;
|
printf("Adding public key to the dtb failed\n");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_fwbin(argv[optind], file, guid, index, instance)
|
if (create_fwbin(argv[optind], file, guid, index, instance)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user