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
|
||||
bool "ARMv7-A, 32bit"
|
||||
select ARCH_SUPPORT_PSCI
|
||||
select BOARD_LATE_INIT
|
||||
select CPU_V7A
|
||||
select SYS_ARCH_TIMER
|
||||
|
||||
config TARGET_QEMU_ARM_64BIT
|
||||
bool "ARMv8, 64bit"
|
||||
select ARM64
|
||||
select BOARD_LATE_INIT
|
||||
|
||||
endchoice
|
||||
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
#ifndef __FSP_SUPPORT_H__
|
||||
#define __FSP_SUPPORT_H__
|
||||
|
||||
#include <signatures.h>
|
||||
|
||||
#include <asm/fsp/fsp_bootmode.h>
|
||||
#include <asm/fsp/fsp_fv.h>
|
||||
#include <asm/fsp/fsp_hob.h>
|
||||
#include <asm/fsp/fsp_infoheader.h>
|
||||
#include <asm/fsp/fsp_types.h>
|
||||
#include <asm/fsp_arch.h>
|
||||
#include <asm/fsp/fsp_azalia.h>
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_late_init(void)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -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(disk-update, 0, 0, do_efi_capsule_on_disk_update,
|
||||
"", ""),
|
||||
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
|
||||
"efidebug capsule update [-v] <capsule address>\n"
|
||||
" - process a capsule\n"
|
||||
"efidebug capsule disk-update\n"
|
||||
" - update a capsule from disk\n"
|
||||
"efidebug capsule show <capsule address>\n"
|
||||
" - show capsule information\n"
|
||||
"efidebug capsule result [<capsule result var>]\n"
|
||||
|
|
|
@ -10,3 +10,4 @@ Emulation
|
|||
qemu-mips
|
||||
qemu-riscv
|
||||
qemu-x86
|
||||
qemu_capsule_update
|
||||
|
|
|
@ -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,
|
||||
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_oob_ops io_op = {};
|
||||
int ret = 0;
|
||||
|
@ -34,7 +34,7 @@ static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
|
|||
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;
|
||||
|
||||
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) {
|
||||
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.addr = off;
|
||||
erase_op.len = mtd->erasesize;
|
||||
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) {
|
||||
if (erase_op.addr + remaining > lim) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
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)
|
||||
{
|
||||
fsdata *mydata = itr->fsdata; /* for silly macros */
|
||||
int ret;
|
||||
u32 sect;
|
||||
u32 read_size;
|
||||
|
@ -838,8 +837,8 @@ void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
|
|||
read_size = itr->fsdata->clust_size;
|
||||
}
|
||||
|
||||
debug("FAT read(sect=%d), clust_size=%d, read_size=%u, DIRENTSPERBLOCK=%zd\n",
|
||||
sect, itr->fsdata->clust_size, read_size, DIRENTSPERBLOCK);
|
||||
log_debug("FAT read(sect=%d), clust_size=%d, read_size=%u\n",
|
||||
sect, itr->fsdata->clust_size, read_size);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
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,
|
||||
.mkdir = fs_mkdir_unsupported,
|
||||
#endif
|
||||
.uuid = fs_uuid_unsupported,
|
||||
.uuid = fat_uuid,
|
||||
.opendir = fat_opendir,
|
||||
.readdir = fat_readdir,
|
||||
.closedir = fat_closedir,
|
||||
|
|
|
@ -1812,6 +1812,24 @@ struct efi_variable_authentication_2 {
|
|||
struct win_certificate_uefi_guid auth_info;
|
||||
} __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
|
||||
*
|
||||
|
|
|
@ -304,15 +304,17 @@ enum efi_image_auth_status {
|
|||
* @exit_status: exit status passed to Exit()
|
||||
* @exit_data_size: exit data size 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
|
||||
* @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_object header;
|
||||
efi_status_t exit_status;
|
||||
efi_status_t *exit_status;
|
||||
efi_uintn_t *exit_data_size;
|
||||
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,
|
||||
struct efi_system_table *st);
|
||||
u16 image_type;
|
||||
|
@ -811,18 +813,27 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
|
|||
int nocheck);
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
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() */
|
||||
void efi_memcpy_runtime(void *dest, const void *src, size_t n);
|
||||
|
||||
/* 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_raw;
|
||||
|
@ -838,6 +849,10 @@ efi_status_t EFIAPI efi_query_capsule_caps(
|
|||
u64 *maximum_capsule_size,
|
||||
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\\"
|
||||
|
||||
/* 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_BUF_SIZE 0x4000
|
||||
#define EFI_VAR_BUF_SIZE CONFIG_EFI_VAR_BUF_SIZE
|
||||
|
||||
/*
|
||||
* 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 DIRENTSPERBLOCK (mydata->sect_size / sizeof(dir_entry))
|
||||
#define DIRENTSPERCLUST ((mydata->clust_size * mydata->sect_size) / \
|
||||
sizeof(dir_entry))
|
||||
|
||||
|
@ -213,4 +212,16 @@ int fat_unlink(const char *filename);
|
|||
int fat_mkdir(const char *dirname);
|
||||
void fat_close(void);
|
||||
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_ */
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __FSP_TYPES_H__
|
||||
#define __FSP_TYPES_H__
|
||||
#ifndef __SIGNATURES_H__
|
||||
#define __SIGNATURES_H__
|
||||
|
||||
/**
|
||||
* 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) \
|
||||
(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];
|
||||
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;
|
||||
|
||||
if (!sinfo->sig->hash_algo)
|
||||
|
@ -63,18 +70,26 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
|||
else
|
||||
return -ENOPKG;
|
||||
|
||||
sig->digest = calloc(1, sig->digest_size);
|
||||
if (!sig->digest) {
|
||||
pr_warn("Sig %u: Out of memory\n", sinfo->index);
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Calculate the hash only if the data is present.
|
||||
* In case of authenticated variable and capsule,
|
||||
* 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
|
||||
* message digest attribute amongst them which corresponds to the
|
||||
* digest we just calculated.
|
||||
|
|
|
@ -77,6 +77,20 @@ config EFI_VAR_SEED_FILE
|
|||
|
||||
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
|
||||
bool "GetTime() runtime service"
|
||||
depends on DM_RTC
|
||||
|
@ -139,6 +153,23 @@ config EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
|||
Select this option if you want to enable capsule-based
|
||||
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
|
||||
bool "FMP driver for FIT image"
|
||||
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);
|
||||
ret = efi_set_load_options(*handle, size, *load_options);
|
||||
} else {
|
||||
load_options = NULL;
|
||||
*load_options = NULL;
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
|
@ -247,8 +247,8 @@ static void efi_queue_event(struct efi_event *event)
|
|||
}
|
||||
if (event)
|
||||
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
|
||||
* @event: event to signal
|
||||
*
|
||||
* 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
|
||||
* 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,
|
||||
* their notification function is queued.
|
||||
*
|
||||
* 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 IS_ENABLED(CONFIG_USB_DEVICE)
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE))
|
||||
udc_disconnect();
|
||||
board_quiesce_devices();
|
||||
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;
|
||||
void *info;
|
||||
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);
|
||||
|
||||
|
@ -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 = exit_data;
|
||||
image_obj->exit_status = &exit_status;
|
||||
image_obj->exit_jmp = &exit_jmp;
|
||||
|
||||
/* 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
|
||||
* 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());
|
||||
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));
|
||||
current_image = parent_image;
|
||||
return EFI_EXIT(image_obj->exit_status);
|
||||
return EFI_EXIT(exit_status);
|
||||
}
|
||||
|
||||
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_obj *image_obj =
|
||||
(struct efi_loaded_image_obj *)image_handle;
|
||||
struct jmp_buf_data *exit_jmp;
|
||||
|
||||
EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
|
||||
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)
|
||||
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 ||
|
||||
exit_status != EFI_SUCCESS)
|
||||
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();
|
||||
|
||||
image_obj->exit_status = exit_status;
|
||||
longjmp(&image_obj->exit_jmp, 1);
|
||||
longjmp(exit_jmp, 1);
|
||||
|
||||
panic("EFI application exited");
|
||||
out:
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#include <mapmem.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;
|
||||
static const efi_guid_t efi_guid_firmware_management_capsule_id =
|
||||
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;
|
||||
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.capsule_guid = capsule->capsule_guid;
|
||||
ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
|
||||
|
@ -191,6 +195,124 @@ skip:
|
|||
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
|
||||
* @capsule_data: Capsule
|
||||
|
@ -896,7 +1018,8 @@ efi_status_t efi_launch_capsules(void)
|
|||
free(files);
|
||||
|
||||
/* 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_VARIABLE_READ_ONLY |
|
||||
EFI_VARIABLE_NON_VOLATILE |
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <env.h>
|
||||
#include <stdio_dev.h>
|
||||
#include <video_console.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define EFI_COUT_MODE_2 2
|
||||
#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) {
|
||||
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
|
||||
* https://www.xfree86.org/4.8.0/ctlseqs.html
|
||||
|
|
|
@ -11,8 +11,30 @@
|
|||
#include <dfu.h>
|
||||
#include <efi_loader.h>
|
||||
#include <image.h>
|
||||
#include <signatures.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 */
|
||||
static
|
||||
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].size = 0;
|
||||
image_info[i].attributes_supported =
|
||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
|
||||
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
|
||||
image_info[i].attributes_setting =
|
||||
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].last_attempt_version = 0;
|
||||
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),
|
||||
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,
|
||||
image_size, vendor_code, progress, abort_reason);
|
||||
|
||||
if (!image)
|
||||
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,
|
||||
NULL, NULL))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
|
|
@ -4,13 +4,11 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <dm.h>
|
||||
#include <fs.h>
|
||||
#include <efi_loader.h>
|
||||
#include <efi_load_initrd.h>
|
||||
#include <fs.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
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)
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_EFI_LOAD_FILE2_INITRD
|
||||
ret = efi_initrd_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_NET
|
||||
ret = efi_net_register();
|
||||
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_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
|
||||
|
@ -651,6 +736,63 @@ err:
|
|||
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
|
||||
* @name: Variable's name
|
||||
|
@ -662,8 +804,7 @@ err:
|
|||
*/
|
||||
struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
|
||||
{
|
||||
struct efi_signature_store *sigstore = NULL, *siglist;
|
||||
struct efi_signature_list *esl;
|
||||
struct efi_signature_store *sigstore = NULL;
|
||||
const efi_guid_t *vendor;
|
||||
void *db;
|
||||
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));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
EFI_PRINT("Getting variable, %ls, failed\n", name);
|
||||
goto err;
|
||||
free(db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parse siglist list */
|
||||
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;
|
||||
return efi_build_signature_store(db, db_size);
|
||||
}
|
||||
#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
|
||||
* 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;
|
||||
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);
|
||||
sprintf(index_buf, "%04X", index);
|
||||
snprintf(index_buf, sizeof(index_buf), "%04X", index);
|
||||
utf8_utf16_strcpy(&p, index_buf);
|
||||
|
||||
return p;
|
||||
|
|
|
@ -24,91 +24,6 @@
|
|||
#include <asm/sections.h>
|
||||
|
||||
#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
|
||||
|
@ -215,10 +130,10 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
|
|||
goto err;
|
||||
|
||||
/* ebuf should be kept valid during the authentication */
|
||||
var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
|
||||
auth->auth_info.hdr.dwLength
|
||||
- sizeof(auth->auth_info),
|
||||
&ebuf);
|
||||
var_sig = efi_parse_pkcs7_header(auth->auth_info.cert_data,
|
||||
auth->auth_info.hdr.dwLength
|
||||
- sizeof(auth->auth_info),
|
||||
&ebuf);
|
||||
if (!var_sig) {
|
||||
EFI_PRINT("Parsing variable's signature failed\n");
|
||||
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;
|
||||
struct udevice *tee = NULL;
|
||||
struct tee_open_session_arg arg;
|
||||
int rc;
|
||||
int rc = -ENODEV;
|
||||
|
||||
tee = tee_find_device(tee, NULL, NULL, NULL);
|
||||
if (!tee)
|
||||
return -ENODEV;
|
||||
goto out;
|
||||
|
||||
memset(&arg, 0, sizeof(arg));
|
||||
tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
|
||||
rc = tee_open_session(tee, &arg, 0, NULL);
|
||||
if (!rc) {
|
||||
conn->tee = tee;
|
||||
conn->session = arg.session;
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
log_err("Unable to register shared memory\n");
|
||||
tee_close_session(conn.tee, conn.session);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -603,7 +603,7 @@ static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts)
|
|||
u16 *pos;
|
||||
|
||||
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(pos - buf, u16_strnlen(buf, SIZE_MAX));
|
||||
|
|
|
@ -218,6 +218,7 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
|
|||
hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
|
||||
HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
|
||||
|
||||
mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
|
||||
hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
|
||||
|
||||
# We build some files with extra pedantic flags to try to minimize things
|
||||
|
|
|
@ -4,16 +4,22 @@
|
|||
* Author: AKASHI Takahiro
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "fdt_host.h"
|
||||
|
||||
typedef __u8 u8;
|
||||
typedef __u16 u16;
|
||||
typedef __u32 u32;
|
||||
|
@ -23,6 +29,9 @@ typedef __s32 s32;
|
|||
|
||||
#define aligned_u64 __aligned_u64
|
||||
|
||||
#define SIGNATURE_NODENAME "signature"
|
||||
#define OVERLAY_NODENAME "__overlay__"
|
||||
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
@ -43,6 +52,9 @@ static struct option options[] = {
|
|||
{"raw", required_argument, NULL, 'r'},
|
||||
{"index", 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'},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
@ -51,14 +63,183 @@ static void print_usage(void)
|
|||
{
|
||||
printf("Usage: %s [options] <output file>\n"
|
||||
"Options:\n"
|
||||
"\t--fit <fit image> new FIT image file\n"
|
||||
"\t--raw <raw image> new raw image file\n"
|
||||
"\t--index <index> update image index\n"
|
||||
"\t--instance <instance> update hardware instance\n"
|
||||
"\t--help print a help message\n",
|
||||
|
||||
"\t--fit <fit image> new FIT image file\n"
|
||||
"\t--raw <raw image> new raw image file\n"
|
||||
"\t--index <index> update image index\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);
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned long index, unsigned long instance)
|
||||
{
|
||||
|
@ -173,16 +354,22 @@ err_1:
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
char *file;
|
||||
char *pkey_file;
|
||||
char *dtb_file;
|
||||
efi_guid_t *guid;
|
||||
unsigned long index, instance;
|
||||
int c, idx;
|
||||
int ret;
|
||||
bool overlay = false;
|
||||
|
||||
file = NULL;
|
||||
pkey_file = NULL;
|
||||
dtb_file = NULL;
|
||||
guid = NULL;
|
||||
index = 0;
|
||||
instance = 0;
|
||||
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)
|
||||
break;
|
||||
|
||||
|
@ -209,22 +396,44 @@ int main(int argc, char **argv)
|
|||
case 'I':
|
||||
instance = strtoul(optarg, NULL, 0);
|
||||
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':
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* need a output file */
|
||||
if (argc != optind + 1) {
|
||||
/* need a fit image file or raw image file */
|
||||
if (!file && !pkey_file && !dtb_file) {
|
||||
printf("%s: %d\n", __func__, __LINE__);
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* need a fit image file or raw image file */
|
||||
if (!file) {
|
||||
print_usage();
|
||||
return -1;
|
||||
if (pkey_file && dtb_file) {
|
||||
ret = add_public_key(pkey_file, dtb_file, overlay);
|
||||
if (ret == -1) {
|
||||
printf("Adding public key to the dtb failed\n");
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (create_fwbin(argv[optind], file, guid, index, instance)
|
||||
|
|
Loading…
Reference in New Issue