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 This pull request adds: * eventlog support for TCG2_PROTOCOL * UEFI capusule updates It replace printf by log in efi_uclass.c
This commit is contained in:
239
cmd/efidebug.c
239
cmd/efidebug.c
@@ -19,6 +19,228 @@
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#define BS systab.boottime
|
||||
#define RT systab.runtime
|
||||
|
||||
#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
|
||||
/**
|
||||
* do_efi_capsule_update() - process a capsule update
|
||||
*
|
||||
* @cmdtp: Command table
|
||||
* @flag: Command flag
|
||||
* @argc: Number of arguments
|
||||
* @argv: Argument array
|
||||
* Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
|
||||
*
|
||||
* Implement efidebug "capsule update" sub-command.
|
||||
* process a capsule update.
|
||||
*
|
||||
* efidebug capsule update [-v] <capsule address>
|
||||
*/
|
||||
static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
struct efi_capsule_header *capsule;
|
||||
int verbose = 0;
|
||||
char *endp;
|
||||
efi_status_t ret;
|
||||
|
||||
if (argc != 2 && argc != 3)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
if (argc == 3) {
|
||||
if (strcmp(argv[1], "-v"))
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
verbose = 1;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16);
|
||||
if (endp == argv[1]) {
|
||||
printf("Invalid address: %s", argv[1]);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
|
||||
printf("Capsule flags: 0x%x\n", capsule->flags);
|
||||
printf("Capsule header size: 0x%x\n", capsule->header_size);
|
||||
printf("Capsule image size: 0x%x\n",
|
||||
capsule->capsule_image_size);
|
||||
}
|
||||
|
||||
ret = EFI_CALL(RT->update_capsule(&capsule, 1, (u64)NULL));
|
||||
if (ret) {
|
||||
printf("Cannot handle a capsule at %p", capsule);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_efi_capsule_show() - show capsule information
|
||||
*
|
||||
* @cmdtp: Command table
|
||||
* @flag: Command flag
|
||||
* @argc: Number of arguments
|
||||
* @argv: Argument array
|
||||
* Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
|
||||
*
|
||||
* Implement efidebug "capsule show" sub-command.
|
||||
* show capsule information.
|
||||
*
|
||||
* efidebug capsule show <capsule address>
|
||||
*/
|
||||
static int do_efi_capsule_show(struct cmd_tbl *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
struct efi_capsule_header *capsule;
|
||||
char *endp;
|
||||
|
||||
if (argc != 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16);
|
||||
if (endp == argv[1]) {
|
||||
printf("Invalid address: %s", argv[1]);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
printf("Capsule guid: %pUl\n", &capsule->capsule_guid);
|
||||
printf("Capsule flags: 0x%x\n", capsule->flags);
|
||||
printf("Capsule header size: 0x%x\n", capsule->header_size);
|
||||
printf("Capsule image size: 0x%x\n",
|
||||
capsule->capsule_image_size);
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_efi_capsule_res() - show a capsule update result
|
||||
*
|
||||
* @cmdtp: Command table
|
||||
* @flag: Command flag
|
||||
* @argc: Number of arguments
|
||||
* @argv: Argument array
|
||||
* Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
|
||||
*
|
||||
* Implement efidebug "capsule result" sub-command.
|
||||
* show a capsule update result.
|
||||
* If result number is not specified, CapsuleLast will be shown.
|
||||
*
|
||||
* efidebug capsule result [<capsule result number>]
|
||||
*/
|
||||
static int do_efi_capsule_res(struct cmd_tbl *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
int capsule_id;
|
||||
char *endp;
|
||||
char var_name[12];
|
||||
u16 var_name16[12], *p;
|
||||
efi_guid_t guid;
|
||||
struct efi_capsule_result_variable_header *result = NULL;
|
||||
efi_uintn_t size;
|
||||
efi_status_t ret;
|
||||
|
||||
if (argc != 1 && argc != 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
guid = efi_guid_capsule_report;
|
||||
if (argc == 1) {
|
||||
size = sizeof(var_name16);
|
||||
ret = EFI_CALL(RT->get_variable(L"CapsuleLast", &guid, NULL,
|
||||
&size, var_name16));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
if (ret == EFI_NOT_FOUND)
|
||||
printf("CapsuleLast doesn't exist\n");
|
||||
else
|
||||
printf("Failed to get CapsuleLast\n");
|
||||
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
printf("CapsuleLast is %ls\n", var_name16);
|
||||
} else {
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
capsule_id = simple_strtoul(argv[0], &endp, 16);
|
||||
if (capsule_id < 0 || capsule_id > 0xffff)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
sprintf(var_name, "Capsule%04X", capsule_id);
|
||||
p = var_name16;
|
||||
utf8_utf16_strncpy(&p, var_name, 9);
|
||||
}
|
||||
|
||||
size = 0;
|
||||
ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, NULL));
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
result = malloc(size);
|
||||
ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size,
|
||||
result));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
free(result);
|
||||
printf("Failed to get %ls\n", var_name16);
|
||||
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Result total size: 0x%x\n", result->variable_total_size);
|
||||
printf("Capsule guid: %pUl\n", &result->capsule_guid);
|
||||
printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n",
|
||||
result->capsule_processed.year, result->capsule_processed.month,
|
||||
result->capsule_processed.day, result->capsule_processed.hour,
|
||||
result->capsule_processed.minute,
|
||||
result->capsule_processed.second);
|
||||
printf("Capsule status: 0x%lx\n", result->capsule_status);
|
||||
|
||||
free(result);
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_tbl cmd_efidebug_capsule_sub[] = {
|
||||
U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update,
|
||||
"", ""),
|
||||
U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
|
||||
"", ""),
|
||||
U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
|
||||
"", ""),
|
||||
};
|
||||
|
||||
/**
|
||||
* do_efi_capsule() - manage UEFI capsules
|
||||
*
|
||||
* @cmdtp: Command table
|
||||
* @flag: Command flag
|
||||
* @argc: Number of arguments
|
||||
* @argv: Argument array
|
||||
* Return: CMD_RET_SUCCESS on success,
|
||||
* CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
|
||||
*
|
||||
* Implement efidebug "capsule" sub-command.
|
||||
*/
|
||||
static int do_efi_capsule(struct cmd_tbl *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
struct cmd_tbl *cp;
|
||||
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
argc--; argv++;
|
||||
|
||||
cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub,
|
||||
ARRAY_SIZE(cmd_efidebug_capsule_sub));
|
||||
if (!cp)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
return cp->cmd(cmdtp, flag, argc, argv);
|
||||
}
|
||||
#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
|
||||
|
||||
/**
|
||||
* efi_get_device_handle_info() - get information of UEFI device
|
||||
@@ -278,6 +500,10 @@ static const struct {
|
||||
"Runtime properties",
|
||||
EFI_RT_PROPERTIES_TABLE_GUID,
|
||||
},
|
||||
{
|
||||
"TCG2 Final Events Table",
|
||||
EFI_TCG2_FINAL_EVENTS_TABLE_GUID,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1237,6 +1463,10 @@ static int do_efi_query_info(struct cmd_tbl *cmdtp, int flag,
|
||||
|
||||
static struct cmd_tbl cmd_efidebug_sub[] = {
|
||||
U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""),
|
||||
#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
|
||||
U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule,
|
||||
"", ""),
|
||||
#endif
|
||||
U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices,
|
||||
"", ""),
|
||||
U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers,
|
||||
@@ -1311,6 +1541,15 @@ static char efidebug_help_text[] =
|
||||
"efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n"
|
||||
" - set/show UEFI boot order\n"
|
||||
"\n"
|
||||
#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
|
||||
"efidebug capsule update [-v] <capsule address>\n"
|
||||
" - process a capsule\n"
|
||||
"efidebug capsule show <capsule address>\n"
|
||||
" - show capsule information\n"
|
||||
"efidebug capsule result [<capsule result var>]\n"
|
||||
" - show a capsule update result\n"
|
||||
"\n"
|
||||
#endif
|
||||
"efidebug devices\n"
|
||||
" - show UEFI devices\n"
|
||||
"efidebug drivers\n"
|
||||
|
||||
@@ -116,7 +116,8 @@ static int do_tpm2_pcr_extend(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
if (index >= priv->pcr_count)
|
||||
return -EINVAL;
|
||||
|
||||
rc = tpm2_pcr_extend(dev, index, digest);
|
||||
rc = tpm2_pcr_extend(dev, index, TPM2_ALG_SHA256, digest,
|
||||
TPM2_DIGEST_LEN);
|
||||
|
||||
unmap_sysmem(digest);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <init.h>
|
||||
#include <net.h>
|
||||
#include <version.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
static void run_preboot_environment_command(void)
|
||||
{
|
||||
@@ -53,6 +54,9 @@ void main_loop(void)
|
||||
if (IS_ENABLED(CONFIG_UPDATE_TFTP))
|
||||
update_tftp(0UL, NULL, NULL);
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
|
||||
efi_launch_capsules();
|
||||
|
||||
s = bootdelay_process();
|
||||
if (cli_process_fdt(&s))
|
||||
cli_secure_boot_cmd(s);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <errno.h>
|
||||
#include <mtd/cfi_flash.h>
|
||||
|
||||
#ifdef CONFIG_DFU_TFTP
|
||||
#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
|
||||
/* env variable holding the location of the update file */
|
||||
#define UPDATE_FILE_ENV "updatefile"
|
||||
|
||||
@@ -99,7 +99,6 @@ static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif /* CONFIG_DFU_TFTP */
|
||||
|
||||
#ifdef CONFIG_MTD_NOR_FLASH
|
||||
static int update_flash_protect(int prot, ulong addr_first, ulong addr_last)
|
||||
@@ -216,6 +215,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */
|
||||
|
||||
static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
|
||||
ulong *fladdr, ulong *size)
|
||||
@@ -233,7 +233,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DFU_TFTP
|
||||
#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
|
||||
int update_tftp(ulong addr, char *interface, char *devstring)
|
||||
{
|
||||
char *filename, *env_addr, *fit_image_name;
|
||||
@@ -340,7 +340,7 @@ next_node:
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_DFU_UPDATE */
|
||||
#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */
|
||||
|
||||
#ifdef CONFIG_UPDATE_FIT
|
||||
/**
|
||||
|
||||
@@ -231,3 +231,9 @@ CONFIG_TEST_FDTDEC=y
|
||||
CONFIG_UNIT_TEST=y
|
||||
CONFIG_UT_TIME=y
|
||||
CONFIG_UT_DM=y
|
||||
#
|
||||
CONFIG_DFU_SF=y
|
||||
CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
|
||||
CONFIG_EFI_CAPSULE_ON_DISK=y
|
||||
CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
|
||||
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
|
||||
|
||||
@@ -274,3 +274,9 @@ CONFIG_TEST_FDTDEC=y
|
||||
CONFIG_UNIT_TEST=y
|
||||
CONFIG_UT_TIME=y
|
||||
CONFIG_UT_DM=y
|
||||
#
|
||||
CONFIG_DFU_SF=y
|
||||
CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
|
||||
CONFIG_EFI_CAPSULE_ON_DISK=y
|
||||
CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
|
||||
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
|
||||
|
||||
@@ -217,6 +217,21 @@ enum efi_reset_type {
|
||||
#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
|
||||
#define CAPSULE_FLAGS_INITIATE_RESET 0x00040000
|
||||
|
||||
#define CAPSULE_SUPPORT_AUTHENTICATION 0x0000000000000001
|
||||
#define CAPSULE_SUPPORT_DEPENDENCY 0x0000000000000002
|
||||
|
||||
#define EFI_CAPSULE_REPORT_GUID \
|
||||
EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \
|
||||
0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
|
||||
|
||||
#define EFI_MEMORY_RANGE_CAPSULE_GUID \
|
||||
EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \
|
||||
0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72)
|
||||
|
||||
#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
|
||||
EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
|
||||
0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
|
||||
|
||||
struct efi_capsule_header {
|
||||
efi_guid_t capsule_guid;
|
||||
u32 header_size;
|
||||
@@ -224,6 +239,54 @@ struct efi_capsule_header {
|
||||
u32 capsule_image_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_capsule_result_variable_header {
|
||||
u32 variable_total_size;
|
||||
u32 reserved;
|
||||
efi_guid_t capsule_guid;
|
||||
struct efi_time capsule_processed;
|
||||
efi_status_t capsule_status;
|
||||
} __packed;
|
||||
|
||||
struct efi_memory_range {
|
||||
efi_physical_addr_t address;
|
||||
u64 length;
|
||||
};
|
||||
|
||||
struct efi_memory_range_capsule {
|
||||
struct efi_capsule_header *header;
|
||||
/* EFI_MEMORY_TYPE: 0x80000000-0xFFFFFFFF */
|
||||
enum efi_mem_type os_requested_memory_type;
|
||||
u64 number_of_memory_ranges;
|
||||
struct efi_memory_range memory_ranges[];
|
||||
} __packed;
|
||||
|
||||
struct efi_firmware_management_capsule_header {
|
||||
u32 version;
|
||||
u16 embedded_driver_count;
|
||||
u16 payload_item_count;
|
||||
u64 item_offset_list[];
|
||||
} __packed;
|
||||
|
||||
struct efi_firmware_management_capsule_image_header {
|
||||
u32 version;
|
||||
efi_guid_t update_image_type_id;
|
||||
u8 update_image_index;
|
||||
u8 reserved[3];
|
||||
u32 update_image_size;
|
||||
u32 update_vendor_code_size;
|
||||
u64 update_hardware_instance;
|
||||
u64 image_capsule_support;
|
||||
} __packed;
|
||||
|
||||
struct efi_capsule_result_variable_fmp {
|
||||
u16 version;
|
||||
u8 payload_index;
|
||||
u8 update_image_index;
|
||||
efi_guid_t update_image_type_id;
|
||||
// u16 capsule_file_name[];
|
||||
// u16 capsule_target[];
|
||||
} __packed;
|
||||
|
||||
#define EFI_RT_SUPPORTED_GET_TIME 0x0001
|
||||
#define EFI_RT_SUPPORTED_SET_TIME 0x0002
|
||||
#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004
|
||||
@@ -356,6 +419,10 @@ struct efi_runtime_services {
|
||||
EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, \
|
||||
0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
|
||||
|
||||
#define EFI_TCG2_FINAL_EVENTS_TABLE_GUID \
|
||||
EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, \
|
||||
0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25)
|
||||
|
||||
struct efi_configuration_table {
|
||||
efi_guid_t guid;
|
||||
void *table;
|
||||
@@ -1779,4 +1846,107 @@ struct efi_signature_list {
|
||||
/* struct efi_signature_data signatures[...][signature_size]; */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/*
|
||||
* Firmware management protocol
|
||||
*/
|
||||
#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \
|
||||
EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \
|
||||
0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7)
|
||||
|
||||
#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
|
||||
EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
|
||||
0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
|
||||
|
||||
#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID \
|
||||
EFI_GUID(0xe2bb9c06, 0x70e9, 0x4b14, 0x97, 0xa3, \
|
||||
0x5a, 0x79, 0x13, 0x17, 0x6e, 0x3f)
|
||||
|
||||
#define IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x0000000000000001
|
||||
#define IMAGE_ATTRIBUTE_RESET_REQUIRED 0x0000000000000002
|
||||
#define IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x0000000000000004
|
||||
#define IMAGE_ATTRIBUTE_IN_USE 0x0000000000000008
|
||||
#define IMAGE_ATTRIBUTE_UEFI_IMAGE 0x0000000000000010
|
||||
#define IMAGE_ATTRIBUTE_DEPENDENCY 0x0000000000000020
|
||||
|
||||
#define IMAGE_COMPATIBILITY_CHECK_SUPPORTED 0x0000000000000001
|
||||
|
||||
#define IMAGE_UPDATABLE_VALID 0x0000000000000001
|
||||
#define IMAGE_UPDATABLE_INVALID 0x0000000000000002
|
||||
#define IMAGE_UPDATABLE_INVALID_TYPE 0x0000000000000004
|
||||
#define IMAGE_UPDATABLE_INVALID_OLLD 0x0000000000000008
|
||||
#define IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE 0x0000000000000010
|
||||
|
||||
#define PACKAGE_ATTRIBUTE_VERSION_UPDATABLE 0x0000000000000001
|
||||
#define PACKAGE_ATTRIBUTE_RESET_REQUIRED 0x0000000000000002
|
||||
#define PACKAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x0000000000000004
|
||||
|
||||
#define EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION 4
|
||||
|
||||
typedef struct efi_firmware_image_dependencies {
|
||||
u8 dependencies[0];
|
||||
} efi_firmware_image_dep_t;
|
||||
|
||||
struct efi_firmware_image_descriptor {
|
||||
u8 image_index;
|
||||
efi_guid_t image_type_id;
|
||||
u64 image_id;
|
||||
u16 *image_id_name;
|
||||
u32 version;
|
||||
u16 *version_name;
|
||||
efi_uintn_t size;
|
||||
u64 attributes_supported;
|
||||
u64 attributes_setting;
|
||||
u64 compatibilities;
|
||||
u32 lowest_supported_image_version;
|
||||
u32 last_attempt_version;
|
||||
u32 last_attempt_status;
|
||||
u64 hardware_instance;
|
||||
efi_firmware_image_dep_t *dependencies;
|
||||
};
|
||||
|
||||
struct efi_firmware_management_protocol {
|
||||
efi_status_t (EFIAPI *get_image_info)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
efi_uintn_t *image_info_size,
|
||||
struct efi_firmware_image_descriptor *image_info,
|
||||
u32 *descriptor_version,
|
||||
u8 *descriptor_count,
|
||||
efi_uintn_t *descriptor_size,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name);
|
||||
efi_status_t (EFIAPI *get_image)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
void *image,
|
||||
efi_uintn_t *image_size);
|
||||
efi_status_t (EFIAPI *set_image)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
const void *image,
|
||||
efi_uintn_t image_size,
|
||||
const void *vendor_code,
|
||||
efi_status_t (*progress)(efi_uintn_t completion),
|
||||
u16 **abort_reason);
|
||||
efi_status_t (EFIAPI *check_image)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
const void *image,
|
||||
efi_uintn_t *image_size,
|
||||
u32 *image_updatable);
|
||||
efi_status_t (EFIAPI *get_package_info)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name,
|
||||
u32 *package_version_name_maxlen,
|
||||
u64 *attributes_supported,
|
||||
u64 *attributes_setting);
|
||||
efi_status_t (EFIAPI *set_package_info)(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
const void *image,
|
||||
efi_uintn_t *image_size,
|
||||
const void *vendor_code,
|
||||
u32 package_version,
|
||||
const u16 *package_version_name);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -210,6 +210,10 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
|
||||
|
||||
/* GUID of RNG protocol */
|
||||
extern const efi_guid_t efi_guid_rng_protocol;
|
||||
/* GUID of capsule update result */
|
||||
extern const efi_guid_t efi_guid_capsule_report;
|
||||
/* GUID of firmware management protocol */
|
||||
extern const efi_guid_t efi_guid_firmware_management_protocol;
|
||||
|
||||
extern unsigned int __efi_runtime_start, __efi_runtime_stop;
|
||||
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
|
||||
@@ -812,6 +816,25 @@ 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);
|
||||
|
||||
extern const struct efi_firmware_management_protocol efi_fmp_fit;
|
||||
extern const struct efi_firmware_management_protocol efi_fmp_raw;
|
||||
|
||||
/* Capsule update */
|
||||
efi_status_t EFIAPI efi_update_capsule(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 scatter_gather_list);
|
||||
efi_status_t EFIAPI efi_query_capsule_caps(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 *maximum_capsule_size,
|
||||
u32 *reset_type);
|
||||
|
||||
#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\"
|
||||
|
||||
/* Hook at initialization */
|
||||
efi_status_t efi_launch_capsules(void);
|
||||
|
||||
#else /* CONFIG_IS_ENABLED(EFI_LOADER) */
|
||||
|
||||
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
|
||||
@@ -828,6 +851,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
|
||||
const char *path) { }
|
||||
static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
|
||||
static inline void efi_print_image_infos(void *pc) { }
|
||||
static inline efi_status_t efi_launch_capsules(void)
|
||||
{
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
/* TPMV2 only */
|
||||
#define TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002
|
||||
#define EFI_TCG2_EXTEND_ONLY 0x0000000000000001
|
||||
#define PE_COFF_IMAGE 0x0000000000000010
|
||||
|
||||
/* Algorithm Registry */
|
||||
#define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001
|
||||
@@ -25,6 +27,10 @@
|
||||
#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008
|
||||
#define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010
|
||||
|
||||
#define EFI_TCG2_FINAL_EVENTS_TABLE_VERSION 1
|
||||
|
||||
#define TPM2_EVENT_LOG_SIZE CONFIG_EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
|
||||
|
||||
typedef u32 efi_tcg_event_log_bitmap;
|
||||
typedef u32 efi_tcg_event_log_format;
|
||||
typedef u32 efi_tcg_event_algorithm_bitmap;
|
||||
@@ -65,6 +71,68 @@ struct efi_tcg2_boot_service_capability {
|
||||
sizeof(struct efi_tcg2_boot_service_capability) - \
|
||||
offsetof(struct efi_tcg2_boot_service_capability, number_of_pcr_banks)
|
||||
|
||||
#define TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03 "Spec ID Event03"
|
||||
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2 2
|
||||
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 0
|
||||
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2 2
|
||||
|
||||
/**
|
||||
* struct TCG_EfiSpecIdEventAlgorithmSize
|
||||
*
|
||||
* @algorithm_id: algorithm defined in enum tpm2_algorithms
|
||||
* @digest_size: size of the algorithm
|
||||
*/
|
||||
struct tcg_efi_spec_id_event_algorithm_size {
|
||||
u16 algorithm_id;
|
||||
u16 digest_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct TCG_EfiSpecIDEventStruct
|
||||
*
|
||||
* @signature: signature, set to Spec ID Event03
|
||||
* @platform_class: class defined in TCG ACPI Specification
|
||||
* Client Common Header.
|
||||
* @spec_version_minor: minor version
|
||||
* @spec_version_major: major version
|
||||
* @spec_version_errata: major version
|
||||
* @uintn_size: size of the efi_uintn_t fields used in various
|
||||
* data structures used in this specification.
|
||||
* 0x01 indicates u32 and 0x02 indicates u64
|
||||
* @number_of_algorithms: hashing algorithms used in this event log
|
||||
* @digest_sizes: array of number_of_algorithms pairs
|
||||
* 1st member defines the algorithm id
|
||||
* 2nd member defines the algorithm size
|
||||
* @vendor_info_size: size in bytes for vendor specific info
|
||||
* @vendor_info: vendor specific info
|
||||
*/
|
||||
struct tcg_efi_spec_id_event {
|
||||
u8 signature[16];
|
||||
u32 platform_class;
|
||||
u8 spec_version_minor;
|
||||
u8 spec_version_major;
|
||||
u8 spec_errata;
|
||||
u8 uintn_size;
|
||||
u32 number_of_algorithms;
|
||||
struct tcg_efi_spec_id_event_algorithm_size digest_sizes[TPM2_NUM_PCR_BANKS];
|
||||
u8 vendor_info_size;
|
||||
/* U-Boot does not provide any vendor info */
|
||||
u8 vendor_info[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct tdEFI_TCG2_FINAL_EVENTS_TABLE
|
||||
* @version: version number for this structure
|
||||
* @number_of_events: number of events recorded after invocation of
|
||||
* GetEventLog()
|
||||
* @event: List of events of type tcg_pcr_event2
|
||||
*/
|
||||
struct efi_tcg2_final_events_table {
|
||||
u64 version;
|
||||
u64 number_of_events;
|
||||
struct tcg_pcr_event2 event[];
|
||||
};
|
||||
|
||||
struct efi_tcg2_protocol {
|
||||
efi_status_t (EFIAPI * get_capability)(struct efi_tcg2_protocol *this,
|
||||
struct efi_tcg2_boot_service_capability *capability);
|
||||
@@ -73,7 +141,8 @@ struct efi_tcg2_protocol {
|
||||
u64 *event_log_location, u64 *event_log_last_entry,
|
||||
bool *event_log_truncated);
|
||||
efi_status_t (EFIAPI * hash_log_extend_event)(struct efi_tcg2_protocol *this,
|
||||
u64 flags, u64 data_to_hash,
|
||||
u64 flags,
|
||||
efi_physical_addr_t data_to_hash,
|
||||
u64 data_to_hash_len,
|
||||
struct efi_tcg2_event *efi_tcg_event);
|
||||
efi_status_t (EFIAPI * submit_command)(struct efi_tcg2_protocol *this,
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
|
||||
#define TPM2_DIGEST_LEN 32
|
||||
|
||||
#define TPM2_SHA1_DIGEST_SIZE 20
|
||||
#define TPM2_SHA256_DIGEST_SIZE 32
|
||||
#define TPM2_SHA384_DIGEST_SIZE 48
|
||||
#define TPM2_SHA512_DIGEST_SIZE 64
|
||||
#define TPM2_SM3_256_DIGEST_SIZE 32
|
||||
|
||||
#define TPM2_MAX_PCRS 32
|
||||
#define TPM2_PCR_SELECT_MAX ((TPM2_MAX_PCRS + 7) / 8)
|
||||
#define TPM2_MAX_CAP_BUFFER 1024
|
||||
@@ -45,6 +51,15 @@
|
||||
#define TPM2_PT_MAX_COMMAND_SIZE (u32)(TPM2_PT_FIXED + 30)
|
||||
#define TPM2_PT_MAX_RESPONSE_SIZE (u32)(TPM2_PT_FIXED + 31)
|
||||
|
||||
/* event types */
|
||||
#define EV_POST_CODE ((u32)0x00000001)
|
||||
#define EV_NO_ACTION ((u32)0x00000003)
|
||||
#define EV_SEPARATOR ((u32)0x00000004)
|
||||
#define EV_S_CRTM_CONTENTS ((u32)0x00000007)
|
||||
#define EV_S_CRTM_VERSION ((u32)0x00000008)
|
||||
#define EV_CPU_MICROCODE ((u32)0x00000009)
|
||||
#define EV_TABLE_OF_DEVICES ((u32)0x0000000B)
|
||||
|
||||
/* TPMS_TAGGED_PROPERTY Structure */
|
||||
struct tpms_tagged_property {
|
||||
u32 property;
|
||||
@@ -86,6 +101,73 @@ struct tpms_capability_data {
|
||||
union tpmu_capabilities data;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* SHA1 Event Log Entry Format
|
||||
*
|
||||
* @pcr_index: PCRIndex event extended to
|
||||
* @event_type: Type of event (see EFI specs)
|
||||
* @digest: Value extended into PCR index
|
||||
* @event_size: Size of event
|
||||
* @event: Event data
|
||||
*/
|
||||
struct tcg_pcr_event {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
u8 digest[TPM2_SHA1_DIGEST_SIZE];
|
||||
u32 event_size;
|
||||
u8 event[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Definition of TPMU_HA Union
|
||||
*/
|
||||
union tmpu_ha {
|
||||
u8 sha1[TPM2_SHA1_DIGEST_SIZE];
|
||||
u8 sha256[TPM2_SHA256_DIGEST_SIZE];
|
||||
u8 sm3_256[TPM2_SM3_256_DIGEST_SIZE];
|
||||
u8 sha384[TPM2_SHA384_DIGEST_SIZE];
|
||||
u8 sha512[TPM2_SHA512_DIGEST_SIZE];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Definition of TPMT_HA Structure
|
||||
*
|
||||
* @hash_alg: Hash algorithm defined in enum tpm2_algorithms
|
||||
* @digest: Digest value for a given algorithm
|
||||
*/
|
||||
struct tpmt_ha {
|
||||
u16 hash_alg;
|
||||
union tmpu_ha digest;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Definition of TPML_DIGEST_VALUES Structure
|
||||
*
|
||||
* @count: Number of algorithms supported by hardware
|
||||
* @digests: struct for algorithm id and hash value
|
||||
*/
|
||||
struct tpml_digest_values {
|
||||
u32 count;
|
||||
struct tpmt_ha digests[TPM2_NUM_PCR_BANKS];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Crypto Agile Log Entry Format
|
||||
*
|
||||
* @pcr_index: PCRIndex event extended to
|
||||
* @event_type: Type of event
|
||||
* @digests: List of digestsextended to PCR index
|
||||
* @event_size: Size of the event data
|
||||
* @event: Event data
|
||||
*/
|
||||
struct tcg_pcr_event2 {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
struct tpml_digest_values digests;
|
||||
u32 event_size;
|
||||
u8 event[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* TPM2 Structure Tags for command/response buffers.
|
||||
*
|
||||
@@ -309,11 +391,14 @@ u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
|
||||
*
|
||||
* @dev TPM device
|
||||
* @index Index of the PCR
|
||||
* @algorithm Algorithm used, defined in 'enum tpm2_algorithms'
|
||||
* @digest Value representing the event to be recorded
|
||||
* @digest_len len of the hash
|
||||
*
|
||||
* @return code of the operation
|
||||
*/
|
||||
u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest);
|
||||
u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
|
||||
const u8 *digest, u32 digest_len);
|
||||
|
||||
/**
|
||||
* Issue a TPM2_PCR_Read command.
|
||||
|
||||
@@ -238,7 +238,7 @@ static efi_status_t EFIAPI efi_uc_stop(
|
||||
}
|
||||
ret = EFI_CALL(systab.boottime->free_pool(entry_buffer));
|
||||
if (ret != EFI_SUCCESS)
|
||||
printf("%s: ERROR: Cannot free pool\n", __func__);
|
||||
log_err("Cannot free EFI memory pool\n");
|
||||
|
||||
/* Detach driver from controller */
|
||||
ret = EFI_CALL(systab.boottime->close_protocol(
|
||||
@@ -260,10 +260,10 @@ static efi_status_t efi_add_driver(struct driver *drv)
|
||||
const struct efi_driver_ops *ops = drv->ops;
|
||||
struct efi_driver_binding_extended_protocol *bp;
|
||||
|
||||
debug("EFI: Adding driver '%s'\n", drv->name);
|
||||
log_debug("Adding EFI driver '%s'\n", drv->name);
|
||||
if (!ops->protocol) {
|
||||
printf("EFI: ERROR: protocol GUID missing for driver '%s'\n",
|
||||
drv->name);
|
||||
log_err("EFI protocol GUID missing for driver '%s'\n",
|
||||
drv->name);
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
bp = calloc(1, sizeof(struct efi_driver_binding_extended_protocol));
|
||||
@@ -305,14 +305,14 @@ efi_status_t efi_driver_init(void)
|
||||
struct driver *drv;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
debug("EFI: Initializing EFI driver framework\n");
|
||||
log_debug("Initializing EFI driver framework\n");
|
||||
for (drv = ll_entry_start(struct driver, driver);
|
||||
drv < ll_entry_end(struct driver, driver); ++drv) {
|
||||
if (drv->id == UCLASS_EFI) {
|
||||
ret = efi_add_driver(drv);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
printf("EFI: ERROR: failed to add driver %s\n",
|
||||
drv->name);
|
||||
log_err("Failed to add EFI driver %s\n",
|
||||
drv->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ efi_status_t efi_driver_init(void)
|
||||
*/
|
||||
static int efi_uc_init(struct uclass *class)
|
||||
{
|
||||
printf("EFI: Initializing UCLASS_EFI\n");
|
||||
log_debug("Initializing UCLASS_EFI\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ static int efi_uc_init(struct uclass *class)
|
||||
*/
|
||||
static int efi_uc_destroy(struct uclass *class)
|
||||
{
|
||||
printf("Destroying UCLASS_EFI\n");
|
||||
log_debug("Destroying UCLASS_EFI\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,74 @@ config EFI_SET_TIME
|
||||
Provide the SetTime() runtime service at boottime. This service
|
||||
can be used by an EFI application to adjust the real time clock.
|
||||
|
||||
config EFI_HAVE_CAPSULE_SUPPORT
|
||||
bool
|
||||
|
||||
config EFI_RUNTIME_UPDATE_CAPSULE
|
||||
bool "UpdateCapsule() runtime service"
|
||||
default n
|
||||
select EFI_HAVE_CAPSULE_SUPPORT
|
||||
help
|
||||
Select this option if you want to use UpdateCapsule and
|
||||
QueryCapsuleCapabilities API's.
|
||||
|
||||
config EFI_CAPSULE_ON_DISK
|
||||
bool "Enable capsule-on-disk support"
|
||||
select EFI_HAVE_CAPSULE_SUPPORT
|
||||
default n
|
||||
help
|
||||
Select this option if you want to use capsule-on-disk feature,
|
||||
that is, capsules can be fetched and executed from files
|
||||
under a specific directory on UEFI system partition instead of
|
||||
via UpdateCapsule API.
|
||||
|
||||
config EFI_CAPSULE_ON_DISK_EARLY
|
||||
bool "Initiate capsule-on-disk at U-Boot boottime"
|
||||
depends on EFI_CAPSULE_ON_DISK
|
||||
default n
|
||||
select EFI_SETUP_EARLY
|
||||
help
|
||||
Normally, without this option enabled, capsules will be
|
||||
executed only at the first time of invoking one of efi command.
|
||||
If this option is enabled, capsules will be enforced to be
|
||||
executed as part of U-Boot initialisation so that they will
|
||||
surely take place whatever is set to distro_bootcmd.
|
||||
|
||||
config EFI_CAPSULE_FIRMWARE
|
||||
bool
|
||||
default n
|
||||
|
||||
config EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||
bool "Capsule: Firmware Management Protocol"
|
||||
depends on EFI_HAVE_CAPSULE_SUPPORT
|
||||
default y
|
||||
help
|
||||
Select this option if you want to enable capsule-based
|
||||
firmware update using Firmware Management Protocol.
|
||||
|
||||
config EFI_CAPSULE_FIRMWARE_FIT
|
||||
bool "FMP driver for FIT image"
|
||||
depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||
depends on FIT
|
||||
select UPDATE_FIT
|
||||
select DFU
|
||||
select EFI_CAPSULE_FIRMWARE
|
||||
default n
|
||||
help
|
||||
Select this option if you want to enable firmware management protocol
|
||||
driver for FIT image
|
||||
|
||||
config EFI_CAPSULE_FIRMWARE_RAW
|
||||
bool "FMP driver for raw image"
|
||||
depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||
select DFU
|
||||
select DFU_WRITE_ALT
|
||||
select EFI_CAPSULE_FIRMWARE
|
||||
default n
|
||||
help
|
||||
Select this option if you want to enable firmware management protocol
|
||||
driver for raw image
|
||||
|
||||
config EFI_DEVICE_PATH_TO_TEXT
|
||||
bool "Device path to text protocol"
|
||||
default y
|
||||
@@ -192,6 +260,15 @@ config EFI_TCG2_PROTOCOL
|
||||
Provide a EFI_TCG2_PROTOCOL implementation using the TPM hardware
|
||||
of the platform.
|
||||
|
||||
config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
|
||||
int "EFI_TCG2_PROTOCOL EventLog size"
|
||||
depends on EFI_TCG2_PROTOCOL
|
||||
default 4096
|
||||
help
|
||||
Define the size of the EventLog for EFI_TCG2_PROTOCOL. Note that
|
||||
this is going to be allocated twice. One for the eventlog it self
|
||||
and one for the configuration table that is required from the spec
|
||||
|
||||
config EFI_LOAD_FILE2_INITRD
|
||||
bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
|
||||
default n
|
||||
|
||||
@@ -23,6 +23,8 @@ endif
|
||||
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
|
||||
obj-y += efi_bootmgr.o
|
||||
obj-y += efi_boottime.o
|
||||
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
|
||||
obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
|
||||
obj-y += efi_console.o
|
||||
obj-y += efi_device_path.o
|
||||
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
|
||||
|
||||
909
lib/efi_loader/efi_capsule.c
Normal file
909
lib/efi_loader/efi_capsule.c
Normal file
@@ -0,0 +1,909 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI Capsule
|
||||
*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
* Author: AKASHI Takahiro
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <efi_variable.h>
|
||||
#include <fs.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <sort.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;
|
||||
const efi_guid_t efi_guid_firmware_management_protocol =
|
||||
EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
|
||||
|
||||
#ifdef CONFIG_EFI_CAPSULE_ON_DISK
|
||||
/* for file system access */
|
||||
static struct efi_file_handle *bootdev_root;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* get_last_capsule - get the last capsule index
|
||||
*
|
||||
* Retrieve the index of the capsule invoked last time from "CapsuleLast"
|
||||
* variable.
|
||||
*
|
||||
* Return:
|
||||
* * > 0 - the last capsule index invoked
|
||||
* * 0xffff - on error, or no capsule invoked yet
|
||||
*/
|
||||
static __maybe_unused unsigned int get_last_capsule(void)
|
||||
{
|
||||
u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
|
||||
char value[11], *p;
|
||||
efi_uintn_t size;
|
||||
unsigned long index = 0xffff;
|
||||
efi_status_t ret;
|
||||
|
||||
size = sizeof(value16);
|
||||
ret = efi_get_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
|
||||
NULL, &size, value16, NULL);
|
||||
if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
|
||||
goto err;
|
||||
|
||||
p = value;
|
||||
utf16_utf8_strcpy(&p, value16);
|
||||
strict_strtoul(&value[7], 16, &index);
|
||||
err:
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_capsule_result - set a result variable
|
||||
* @capsule: Capsule
|
||||
* @return_status: Return status
|
||||
*
|
||||
* Create and set a result variable, "CapsuleXXXX", for the capsule,
|
||||
* @capsule.
|
||||
*/
|
||||
static __maybe_unused
|
||||
void set_capsule_result(int index, struct efi_capsule_header *capsule,
|
||||
efi_status_t return_status)
|
||||
{
|
||||
u16 variable_name16[12];
|
||||
struct efi_capsule_result_variable_header result;
|
||||
struct efi_time time;
|
||||
efi_status_t ret;
|
||||
|
||||
efi_create_indexed_name(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));
|
||||
if (ret == EFI_SUCCESS)
|
||||
memcpy(&result.capsule_processed, &time, sizeof(time));
|
||||
else
|
||||
memset(&result.capsule_processed, 0, sizeof(time));
|
||||
result.capsule_status = return_status;
|
||||
ret = efi_set_variable(variable_name16, &efi_guid_capsule_report,
|
||||
EFI_VARIABLE_NON_VOLATILE |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
sizeof(result), &result);
|
||||
if (ret)
|
||||
log_err("EFI: creating %ls failed\n", variable_name16);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
||||
/**
|
||||
* efi_fmp_find - search for Firmware Management Protocol drivers
|
||||
* @image_type: Image type guid
|
||||
* @instance: Instance number
|
||||
* @handles: Handles of FMP drivers
|
||||
* @no_handles: Number of handles
|
||||
*
|
||||
* Search for Firmware Management Protocol drivers, matching the image
|
||||
* type, @image_type and the machine instance, @instance, from the list,
|
||||
* @handles.
|
||||
*
|
||||
* Return:
|
||||
* * Protocol instance - on success
|
||||
* * NULL - on failure
|
||||
*/
|
||||
static struct efi_firmware_management_protocol *
|
||||
efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles,
|
||||
efi_uintn_t no_handles)
|
||||
{
|
||||
efi_handle_t *handle;
|
||||
struct efi_firmware_management_protocol *fmp;
|
||||
struct efi_firmware_image_descriptor *image_info, *desc;
|
||||
efi_uintn_t info_size, descriptor_size;
|
||||
u32 descriptor_version;
|
||||
u8 descriptor_count;
|
||||
u32 package_version;
|
||||
u16 *package_version_name;
|
||||
bool found = false;
|
||||
int i, j;
|
||||
efi_status_t ret;
|
||||
|
||||
for (i = 0, handle = handles; i < no_handles; i++, handle++) {
|
||||
ret = EFI_CALL(efi_handle_protocol(
|
||||
*handle,
|
||||
&efi_guid_firmware_management_protocol,
|
||||
(void **)&fmp));
|
||||
if (ret != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* get device's image info */
|
||||
info_size = 0;
|
||||
image_info = NULL;
|
||||
descriptor_version = 0;
|
||||
descriptor_count = 0;
|
||||
descriptor_size = 0;
|
||||
package_version = 0;
|
||||
package_version_name = NULL;
|
||||
ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
|
||||
image_info,
|
||||
&descriptor_version,
|
||||
&descriptor_count,
|
||||
&descriptor_size,
|
||||
&package_version,
|
||||
&package_version_name));
|
||||
if (ret != EFI_BUFFER_TOO_SMALL)
|
||||
goto skip;
|
||||
|
||||
image_info = malloc(info_size);
|
||||
if (!image_info)
|
||||
goto skip;
|
||||
|
||||
ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
|
||||
image_info,
|
||||
&descriptor_version,
|
||||
&descriptor_count,
|
||||
&descriptor_size,
|
||||
&package_version,
|
||||
&package_version_name));
|
||||
if (ret != EFI_SUCCESS ||
|
||||
descriptor_version != EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION)
|
||||
goto skip;
|
||||
|
||||
/* matching */
|
||||
for (j = 0, desc = image_info; j < descriptor_count;
|
||||
j++, desc = (void *)desc + descriptor_size) {
|
||||
log_debug("+++ desc[%d] index: %d, name: %ls\n",
|
||||
j, desc->image_index, desc->image_id_name);
|
||||
if (!guidcmp(&desc->image_type_id, image_type) &&
|
||||
(!instance ||
|
||||
!desc->hardware_instance ||
|
||||
desc->hardware_instance == instance))
|
||||
found = true;
|
||||
}
|
||||
|
||||
skip:
|
||||
efi_free_pool(package_version_name);
|
||||
free(image_info);
|
||||
EFI_CALL(efi_close_protocol(
|
||||
(efi_handle_t)fmp,
|
||||
&efi_guid_firmware_management_protocol,
|
||||
NULL, NULL));
|
||||
if (found)
|
||||
return fmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_capsule_update_firmware - update firmware from capsule
|
||||
* @capsule_data: Capsule
|
||||
*
|
||||
* Update firmware, using a capsule, @capsule_data. Loading any FMP
|
||||
* drivers embedded in a capsule is not supported.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_capsule_update_firmware(
|
||||
struct efi_capsule_header *capsule_data)
|
||||
{
|
||||
struct efi_firmware_management_capsule_header *capsule;
|
||||
struct efi_firmware_management_capsule_image_header *image;
|
||||
size_t capsule_size;
|
||||
void *image_binary, *vendor_code;
|
||||
efi_handle_t *handles;
|
||||
efi_uintn_t no_handles;
|
||||
int item;
|
||||
struct efi_firmware_management_protocol *fmp;
|
||||
u16 *abort_reason;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
/* sanity check */
|
||||
if (capsule_data->header_size < sizeof(*capsule) ||
|
||||
capsule_data->header_size >= capsule_data->capsule_image_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
capsule = (void *)capsule_data + capsule_data->header_size;
|
||||
capsule_size = capsule_data->capsule_image_size
|
||||
- capsule_data->header_size;
|
||||
|
||||
if (capsule->version != 0x00000001)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
handles = NULL;
|
||||
ret = EFI_CALL(efi_locate_handle_buffer(
|
||||
BY_PROTOCOL,
|
||||
&efi_guid_firmware_management_protocol,
|
||||
NULL, &no_handles, (efi_handle_t **)&handles));
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
/* Payload */
|
||||
for (item = capsule->embedded_driver_count;
|
||||
item < capsule->embedded_driver_count
|
||||
+ capsule->payload_item_count; item++) {
|
||||
/* sanity check */
|
||||
if ((capsule->item_offset_list[item] + sizeof(*image)
|
||||
>= capsule_size)) {
|
||||
log_err("EFI: A capsule has not enough data\n");
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
image = (void *)capsule + capsule->item_offset_list[item];
|
||||
|
||||
if (image->version != 0x00000003) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find a device for update firmware */
|
||||
/* TODO: should we pass index as well, or nothing but type? */
|
||||
fmp = efi_fmp_find(&image->update_image_type_id,
|
||||
image->update_hardware_instance,
|
||||
handles, no_handles);
|
||||
if (!fmp) {
|
||||
log_err("EFI Capsule: driver not found for firmware type: %pUl, hardware instance: %lld\n",
|
||||
&image->update_image_type_id,
|
||||
image->update_hardware_instance);
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* do update */
|
||||
image_binary = (void *)image + sizeof(*image);
|
||||
vendor_code = image_binary + image->update_image_size;
|
||||
|
||||
abort_reason = NULL;
|
||||
ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
|
||||
image_binary,
|
||||
image->update_image_size,
|
||||
vendor_code, NULL,
|
||||
&abort_reason));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
log_err("EFI Capsule: firmware update failed: %ls\n",
|
||||
abort_reason);
|
||||
efi_free_pool(abort_reason);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
efi_free_pool(handles);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static efi_status_t efi_capsule_update_firmware(
|
||||
struct efi_capsule_header *capsule_data)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */
|
||||
|
||||
/**
|
||||
* efi_update_capsule() - process information from operating system
|
||||
* @capsule_header_array: Array of virtual address pointers
|
||||
* @capsule_count: Number of pointers in capsule_header_array
|
||||
* @scatter_gather_list: Array of physical address pointers
|
||||
*
|
||||
* This function implements the UpdateCapsule() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_update_capsule(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 scatter_gather_list)
|
||||
{
|
||||
struct efi_capsule_header *capsule;
|
||||
unsigned int i;
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
|
||||
scatter_gather_list);
|
||||
|
||||
if (!capsule_count) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = EFI_SUCCESS;
|
||||
for (i = 0, capsule = *capsule_header_array; i < capsule_count;
|
||||
i++, capsule = *(++capsule_header_array)) {
|
||||
/* sanity check */
|
||||
if (capsule->header_size < sizeof(*capsule) ||
|
||||
capsule->capsule_image_size < sizeof(*capsule)) {
|
||||
log_err("EFI: A capsule has not enough data\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Capsule[%d] (guid:%pUl)\n",
|
||||
i, &capsule->capsule_guid);
|
||||
if (!guidcmp(&capsule->capsule_guid,
|
||||
&efi_guid_firmware_management_capsule_id)) {
|
||||
ret = efi_capsule_update_firmware(capsule);
|
||||
} else {
|
||||
log_err("EFI: not support capsule type: %pUl\n",
|
||||
&capsule->capsule_guid);
|
||||
ret = EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_query_capsule_caps() - check if capsule is supported
|
||||
* @capsule_header_array: Array of virtual pointers
|
||||
* @capsule_count: Number of pointers in capsule_header_array
|
||||
* @maximum_capsule_size: Maximum capsule size
|
||||
* @reset_type: Type of reset needed for capsule update
|
||||
*
|
||||
* This function implements the QueryCapsuleCapabilities() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_query_capsule_caps(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 *maximum_capsule_size,
|
||||
u32 *reset_type)
|
||||
{
|
||||
struct efi_capsule_header *capsule __attribute__((unused));
|
||||
unsigned int i;
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
|
||||
maximum_capsule_size, reset_type);
|
||||
|
||||
if (!maximum_capsule_size) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*maximum_capsule_size = U64_MAX;
|
||||
*reset_type = EFI_RESET_COLD;
|
||||
|
||||
ret = EFI_SUCCESS;
|
||||
for (i = 0, capsule = *capsule_header_array; i < capsule_count;
|
||||
i++, capsule = *(++capsule_header_array)) {
|
||||
/* TODO */
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EFI_CAPSULE_ON_DISK
|
||||
/**
|
||||
* get_dp_device - retrieve a device path from boot variable
|
||||
* @boot_var: Boot variable name
|
||||
* @device_dp Device path
|
||||
*
|
||||
* Retrieve a device patch from boot variable, @boot_var.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t get_dp_device(u16 *boot_var,
|
||||
struct efi_device_path **device_dp)
|
||||
{
|
||||
void *buf = NULL;
|
||||
efi_uintn_t size;
|
||||
struct efi_load_option lo;
|
||||
struct efi_device_path *file_dp;
|
||||
efi_status_t ret;
|
||||
|
||||
size = 0;
|
||||
ret = efi_get_variable_int(boot_var, &efi_global_variable_guid,
|
||||
NULL, &size, NULL, NULL);
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
buf = malloc(size);
|
||||
if (!buf)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
ret = efi_get_variable_int(boot_var, &efi_global_variable_guid,
|
||||
NULL, &size, buf, NULL);
|
||||
}
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
efi_deserialize_load_option(&lo, buf, &size);
|
||||
|
||||
if (lo.attributes & LOAD_OPTION_ACTIVE) {
|
||||
efi_dp_split_file_path(lo.file_path, device_dp, &file_dp);
|
||||
efi_free_pool(file_dp);
|
||||
|
||||
ret = EFI_SUCCESS;
|
||||
} else {
|
||||
ret = EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_is_present_and_system_part - check if a device exists
|
||||
* @dp Device path
|
||||
*
|
||||
* Check if a device pointed to by the device path, @dp, exists and is
|
||||
* located in UEFI system partition.
|
||||
*
|
||||
* Return: true - yes, false - no
|
||||
*/
|
||||
static bool device_is_present_and_system_part(struct efi_device_path *dp)
|
||||
{
|
||||
efi_handle_t handle;
|
||||
|
||||
handle = efi_dp_find_obj(dp, NULL);
|
||||
if (!handle)
|
||||
return false;
|
||||
|
||||
return efi_disk_is_system_part(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* find_boot_device - identify the boot device
|
||||
*
|
||||
* Identify the boot device from boot-related variables as UEFI
|
||||
* specification describes and put its handle into bootdev_root.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t find_boot_device(void)
|
||||
{
|
||||
char boot_var[9];
|
||||
u16 boot_var16[9], *p, bootnext, *boot_order = NULL;
|
||||
efi_uintn_t size;
|
||||
int i, num;
|
||||
struct efi_simple_file_system_protocol *volume;
|
||||
struct efi_device_path *boot_dev = NULL;
|
||||
efi_status_t ret;
|
||||
|
||||
/* find active boot device in BootNext */
|
||||
bootnext = 0;
|
||||
size = sizeof(bootnext);
|
||||
ret = efi_get_variable_int(L"BootNext",
|
||||
(efi_guid_t *)&efi_global_variable_guid,
|
||||
NULL, &size, &bootnext, NULL);
|
||||
if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
|
||||
/* BootNext does exist here */
|
||||
if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
|
||||
log_err("BootNext must be 16-bit integer\n");
|
||||
goto skip;
|
||||
}
|
||||
sprintf((char *)boot_var, "Boot%04X", bootnext);
|
||||
p = boot_var16;
|
||||
utf8_utf16_strcpy(&p, boot_var);
|
||||
|
||||
ret = get_dp_device(boot_var16, &boot_dev);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
if (device_is_present_and_system_part(boot_dev)) {
|
||||
goto out;
|
||||
} else {
|
||||
efi_free_pool(boot_dev);
|
||||
boot_dev = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
/* find active boot device in BootOrder */
|
||||
size = 0;
|
||||
ret = efi_get_variable_int(L"BootOrder", &efi_global_variable_guid,
|
||||
NULL, &size, NULL, NULL);
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
boot_order = malloc(size);
|
||||
if (!boot_order) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = efi_get_variable_int(L"BootOrder",
|
||||
&efi_global_variable_guid,
|
||||
NULL, &size, boot_order, NULL);
|
||||
}
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* check in higher order */
|
||||
num = size / sizeof(u16);
|
||||
for (i = 0; i < num; i++) {
|
||||
sprintf((char *)boot_var, "Boot%04X", boot_order[i]);
|
||||
p = boot_var16;
|
||||
utf8_utf16_strcpy(&p, boot_var);
|
||||
ret = get_dp_device(boot_var16, &boot_dev);
|
||||
if (ret != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
if (device_is_present_and_system_part(boot_dev))
|
||||
break;
|
||||
|
||||
efi_free_pool(boot_dev);
|
||||
boot_dev = NULL;
|
||||
}
|
||||
out:
|
||||
if (boot_dev) {
|
||||
u16 *path_str;
|
||||
|
||||
path_str = efi_dp_str(boot_dev);
|
||||
log_debug("EFI Capsule: bootdev is %ls\n", path_str);
|
||||
efi_free_pool(path_str);
|
||||
|
||||
volume = efi_fs_from_path(boot_dev);
|
||||
if (!volume)
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
else
|
||||
ret = EFI_CALL(volume->open_volume(volume,
|
||||
&bootdev_root));
|
||||
efi_free_pool(boot_dev);
|
||||
} else {
|
||||
ret = EFI_NOT_FOUND;
|
||||
}
|
||||
free(boot_order);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_capsule_scan_dir - traverse a capsule directory in boot device
|
||||
* @files: Array of file names
|
||||
* @num: Number of elements in @files
|
||||
*
|
||||
* Traverse a capsule directory in boot device.
|
||||
* Called by initialization code, and returns an array of capsule file
|
||||
* names in @files.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num)
|
||||
{
|
||||
struct efi_file_handle *dirh;
|
||||
struct efi_file_info *dirent;
|
||||
efi_uintn_t dirent_size, tmp_size;
|
||||
unsigned int count;
|
||||
u16 **tmp_files;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = find_boot_device();
|
||||
if (ret == EFI_NOT_FOUND) {
|
||||
log_debug("EFI Capsule: bootdev is not set\n");
|
||||
*num = 0;
|
||||
return EFI_SUCCESS;
|
||||
} else if (ret != EFI_SUCCESS) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
/* count capsule files */
|
||||
ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
|
||||
EFI_CAPSULE_DIR,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
*num = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
dirent_size = 256;
|
||||
dirent = malloc(dirent_size);
|
||||
if (!dirent)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
count = 0;
|
||||
while (1) {
|
||||
tmp_size = dirent_size;
|
||||
ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
dirent = realloc(dirent, tmp_size);
|
||||
if (!dirent) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto err;
|
||||
}
|
||||
dirent_size = tmp_size;
|
||||
ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
|
||||
}
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto err;
|
||||
if (!tmp_size)
|
||||
break;
|
||||
|
||||
if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
|
||||
u16_strcmp(dirent->file_name, L".") &&
|
||||
u16_strcmp(dirent->file_name, L".."))
|
||||
count++;
|
||||
}
|
||||
|
||||
ret = EFI_CALL((*dirh->setpos)(dirh, 0));
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto err;
|
||||
|
||||
/* make a list */
|
||||
tmp_files = malloc(count * sizeof(*files));
|
||||
if (!tmp_files) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto err;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
while (1) {
|
||||
tmp_size = dirent_size;
|
||||
ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto err;
|
||||
if (!tmp_size)
|
||||
break;
|
||||
|
||||
if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
|
||||
u16_strcmp(dirent->file_name, L".") &&
|
||||
u16_strcmp(dirent->file_name, L".."))
|
||||
tmp_files[count++] = u16_strdup(dirent->file_name);
|
||||
}
|
||||
/* ignore an error */
|
||||
EFI_CALL((*dirh->close)(dirh));
|
||||
|
||||
/* in ascii order */
|
||||
/* FIXME: u16 version of strcasecmp */
|
||||
qsort(tmp_files, count, sizeof(*tmp_files),
|
||||
(int (*)(const void *, const void *))strcasecmp);
|
||||
*files = tmp_files;
|
||||
*num = count;
|
||||
ret = EFI_SUCCESS;
|
||||
err:
|
||||
free(dirent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_capsule_read_file - read in a capsule file
|
||||
* @filename: File name
|
||||
* @capsule: Pointer to buffer for capsule
|
||||
*
|
||||
* Read a capsule file and put its content in @capsule.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_capsule_read_file(const u16 *filename,
|
||||
struct efi_capsule_header **capsule)
|
||||
{
|
||||
struct efi_file_handle *dirh, *fh;
|
||||
struct efi_file_info *file_info = NULL;
|
||||
struct efi_capsule_header *buf = NULL;
|
||||
efi_uintn_t size;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
|
||||
EFI_CAPSULE_DIR,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
/* ignore an error */
|
||||
EFI_CALL((*dirh->close)(dirh));
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/* file size */
|
||||
size = 0;
|
||||
ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
|
||||
&size, file_info));
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
file_info = malloc(size);
|
||||
if (!file_info) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto err;
|
||||
}
|
||||
ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
|
||||
&size, file_info));
|
||||
}
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto err;
|
||||
size = file_info->file_size;
|
||||
free(file_info);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* fetch data */
|
||||
ret = EFI_CALL((*fh->read)(fh, &size, buf));
|
||||
if (ret == EFI_SUCCESS) {
|
||||
if (size >= buf->capsule_image_size) {
|
||||
*capsule = buf;
|
||||
} else {
|
||||
free(buf);
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
free(buf);
|
||||
}
|
||||
err:
|
||||
EFI_CALL((*fh->close)(fh));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_capsule_delete_file - delete a capsule file
|
||||
* @filename: File name
|
||||
*
|
||||
* Delete a capsule file from capsule directory.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_capsule_delete_file(const u16 *filename)
|
||||
{
|
||||
struct efi_file_handle *dirh, *fh;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
|
||||
EFI_CAPSULE_DIR,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
/* ignore an error */
|
||||
EFI_CALL((*dirh->close)(dirh));
|
||||
|
||||
ret = EFI_CALL((*fh->delete)(fh));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_capsule_scan_done - reset a scan help function
|
||||
*
|
||||
* Reset a scan help function
|
||||
*/
|
||||
static void efi_capsule_scan_done(void)
|
||||
{
|
||||
EFI_CALL((*bootdev_root->close)(bootdev_root));
|
||||
bootdev_root = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_efi_load_capsule_drivers - initialize capsule drivers
|
||||
*
|
||||
* Architecture or board specific initialization routine
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t __weak arch_efi_load_capsule_drivers(void)
|
||||
{
|
||||
__maybe_unused efi_handle_t handle;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_FIT)) {
|
||||
handle = NULL;
|
||||
ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
|
||||
&handle, &efi_guid_firmware_management_protocol,
|
||||
&efi_fmp_fit, NULL));
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) {
|
||||
handle = NULL;
|
||||
ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
|
||||
&efi_root,
|
||||
&efi_guid_firmware_management_protocol,
|
||||
&efi_fmp_raw, NULL));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_launch_capsule - launch capsules
|
||||
*
|
||||
* Launch all the capsules in system at boot time.
|
||||
* Called by efi init code
|
||||
*
|
||||
* Return: status codde
|
||||
*/
|
||||
efi_status_t efi_launch_capsules(void)
|
||||
{
|
||||
u64 os_indications;
|
||||
efi_uintn_t size;
|
||||
struct efi_capsule_header *capsule = NULL;
|
||||
u16 **files;
|
||||
unsigned int nfiles, index, i;
|
||||
u16 variable_name16[12];
|
||||
efi_status_t ret;
|
||||
|
||||
size = sizeof(os_indications);
|
||||
ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid,
|
||||
NULL, &size, &os_indications, NULL);
|
||||
if (ret != EFI_SUCCESS ||
|
||||
!(os_indications
|
||||
& EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))
|
||||
return EFI_SUCCESS;
|
||||
|
||||
index = get_last_capsule();
|
||||
|
||||
/* Load capsule drivers */
|
||||
ret = arch_efi_load_capsule_drivers();
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Find capsules on disk.
|
||||
* All the capsules are collected at the beginning because
|
||||
* capsule files will be removed instantly.
|
||||
*/
|
||||
nfiles = 0;
|
||||
files = NULL;
|
||||
ret = efi_capsule_scan_dir(&files, &nfiles);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
if (!nfiles)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
/* Launch capsules */
|
||||
for (i = 0, ++index; i < nfiles; i++, index++) {
|
||||
log_debug("capsule from %ls ...\n", files[i]);
|
||||
if (index > 0xffff)
|
||||
index = 0;
|
||||
ret = efi_capsule_read_file(files[i], &capsule);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
|
||||
if (ret != EFI_SUCCESS)
|
||||
log_err("EFI Capsule update failed at %ls\n",
|
||||
files[i]);
|
||||
|
||||
free(capsule);
|
||||
} else {
|
||||
log_err("EFI: reading capsule failed: %ls\n", files[i]);
|
||||
}
|
||||
/* create CapsuleXXXX */
|
||||
set_capsule_result(index, capsule, ret);
|
||||
|
||||
/* delete a capsule either in case of success or failure */
|
||||
ret = efi_capsule_delete_file(files[i]);
|
||||
if (ret != EFI_SUCCESS)
|
||||
log_err("EFI: deleting a capsule file failed: %ls\n",
|
||||
files[i]);
|
||||
}
|
||||
efi_capsule_scan_done();
|
||||
|
||||
for (i = 0; i < nfiles; i++)
|
||||
free(files[i]);
|
||||
free(files);
|
||||
|
||||
/* CapsuleLast */
|
||||
efi_create_indexed_name(variable_name16, "Capsule", index - 1);
|
||||
efi_set_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
|
||||
EFI_VARIABLE_READ_ONLY |
|
||||
EFI_VARIABLE_NON_VOLATILE |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
22, variable_name16, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
|
||||
403
lib/efi_loader/efi_firmware.c
Normal file
403
lib/efi_loader/efi_firmware.c
Normal file
@@ -0,0 +1,403 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI Firmware management protocol
|
||||
*
|
||||
* Copyright (c) 2020 Linaro Limited
|
||||
* Author: AKASHI Takahiro
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <charset.h>
|
||||
#include <dfu.h>
|
||||
#include <efi_loader.h>
|
||||
#include <image.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* Place holder; not supported */
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_get_image_unsupported(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
void *image,
|
||||
efi_uintn_t *image_size)
|
||||
{
|
||||
EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/* Place holder; not supported */
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_check_image_unsupported(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
const void *image,
|
||||
efi_uintn_t *image_size,
|
||||
u32 *image_updatable)
|
||||
{
|
||||
EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size,
|
||||
image_updatable);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/* Place holder; not supported */
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_get_package_info_unsupported(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name,
|
||||
u32 *package_version_name_maxlen,
|
||||
u64 *attributes_supported,
|
||||
u64 *attributes_setting)
|
||||
{
|
||||
EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version,
|
||||
package_version_name, package_version_name_maxlen,
|
||||
attributes_supported, attributes_setting);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/* Place holder; not supported */
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
const void *image,
|
||||
efi_uintn_t *image_size,
|
||||
const void *vendor_code,
|
||||
u32 package_version,
|
||||
const u16 *package_version_name)
|
||||
{
|
||||
EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code,
|
||||
package_version, package_version_name);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_dfu_info - return information about the current firmware image
|
||||
* @this: Protocol instance
|
||||
* @image_info_size: Size of @image_info
|
||||
* @image_info: Image information
|
||||
* @descriptor_version: Pointer to version number
|
||||
* @descriptor_count: Pointer to number of descriptors
|
||||
* @descriptor_size: Pointer to descriptor size
|
||||
* package_version: Package version
|
||||
* package_version_name: Package version's name
|
||||
* image_type: Image type GUID
|
||||
*
|
||||
* Return information bout the current firmware image in @image_info.
|
||||
* @image_info will consist of a number of descriptors.
|
||||
* Each descriptor will be created based on "dfu_alt_info" variable.
|
||||
*
|
||||
* Return status code
|
||||
*/
|
||||
static efi_status_t efi_get_dfu_info(
|
||||
efi_uintn_t *image_info_size,
|
||||
struct efi_firmware_image_descriptor *image_info,
|
||||
u32 *descriptor_version,
|
||||
u8 *descriptor_count,
|
||||
efi_uintn_t *descriptor_size,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name,
|
||||
const efi_guid_t *image_type)
|
||||
{
|
||||
struct dfu_entity *dfu;
|
||||
size_t names_len, total_size;
|
||||
int dfu_num, i;
|
||||
u16 *name, *next;
|
||||
|
||||
dfu_init_env_entities(NULL, NULL);
|
||||
|
||||
names_len = 0;
|
||||
dfu_num = 0;
|
||||
list_for_each_entry(dfu, &dfu_list, list) {
|
||||
names_len += (utf8_utf16_strlen(dfu->name) + 1) * 2;
|
||||
dfu_num++;
|
||||
}
|
||||
if (!dfu_num) {
|
||||
log_warning("Probably dfu_alt_info not defined\n");
|
||||
*image_info_size = 0;
|
||||
dfu_free_entities();
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
total_size = sizeof(*image_info) * dfu_num + names_len;
|
||||
/*
|
||||
* we will assume that sizeof(*image_info) * dfu_name
|
||||
* is, at least, a multiple of 2. So the start address for
|
||||
* image_id_name would be aligned with 2 bytes.
|
||||
*/
|
||||
if (*image_info_size < total_size) {
|
||||
*image_info_size = total_size;
|
||||
dfu_free_entities();
|
||||
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
*image_info_size = total_size;
|
||||
|
||||
*descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
|
||||
*descriptor_count = dfu_num;
|
||||
*descriptor_size = sizeof(*image_info);
|
||||
*package_version = 0xffffffff; /* not supported */
|
||||
*package_version_name = NULL; /* not supported */
|
||||
|
||||
/* DFU alt number should correspond to image_index */
|
||||
i = 0;
|
||||
/* Name area starts just after descriptors */
|
||||
name = (u16 *)((u8 *)image_info + sizeof(*image_info) * dfu_num);
|
||||
next = name;
|
||||
list_for_each_entry(dfu, &dfu_list, list) {
|
||||
image_info[i].image_index = dfu->alt + 1;
|
||||
image_info[i].image_type_id = *image_type;
|
||||
image_info[i].image_id = dfu->alt;
|
||||
|
||||
/* copy the DFU entity name */
|
||||
utf8_utf16_strcpy(&next, dfu->name);
|
||||
image_info[i].image_id_name = name;
|
||||
name = ++next;
|
||||
|
||||
image_info[i].version = 0; /* not supported */
|
||||
image_info[i].version_name = NULL; /* not supported */
|
||||
image_info[i].size = 0;
|
||||
image_info[i].attributes_supported =
|
||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
||||
image_info[i].attributes_setting =
|
||||
IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
|
||||
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;
|
||||
image_info[i].hardware_instance = 1;
|
||||
image_info[i].dependencies = NULL;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
dfu_free_entities();
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
|
||||
/*
|
||||
* This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
|
||||
* method with existing FIT image format, and handles
|
||||
* - multiple regions of firmware via DFU
|
||||
* but doesn't support
|
||||
* - versioning of firmware image
|
||||
* - package information
|
||||
*/
|
||||
const efi_guid_t efi_firmware_image_type_uboot_fit =
|
||||
EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
|
||||
|
||||
/**
|
||||
* efi_firmware_fit_get_image_info - return information about the current
|
||||
* firmware image
|
||||
* @this: Protocol instance
|
||||
* @image_info_size: Size of @image_info
|
||||
* @image_info: Image information
|
||||
* @descriptor_version: Pointer to version number
|
||||
* @descriptor_count: Pointer to number of descriptors
|
||||
* @descriptor_size: Pointer to descriptor size
|
||||
* package_version: Package version
|
||||
* package_version_name: Package version's name
|
||||
*
|
||||
* Return information bout the current firmware image in @image_info.
|
||||
* @image_info will consist of a number of descriptors.
|
||||
* Each descriptor will be created based on "dfu_alt_info" variable.
|
||||
*
|
||||
* Return status code
|
||||
*/
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_fit_get_image_info(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
efi_uintn_t *image_info_size,
|
||||
struct efi_firmware_image_descriptor *image_info,
|
||||
u32 *descriptor_version,
|
||||
u8 *descriptor_count,
|
||||
efi_uintn_t *descriptor_size,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
|
||||
image_info_size, image_info,
|
||||
descriptor_version, descriptor_count, descriptor_size,
|
||||
package_version, package_version_name);
|
||||
|
||||
if (!image_info_size)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
if (*image_info_size &&
|
||||
(!image_info || !descriptor_version || !descriptor_count ||
|
||||
!descriptor_size || !package_version || !package_version_name))
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
ret = efi_get_dfu_info(image_info_size, image_info,
|
||||
descriptor_version, descriptor_count,
|
||||
descriptor_size,
|
||||
package_version, package_version_name,
|
||||
&efi_firmware_image_type_uboot_fit);
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_firmware_fit_set_image - update the firmware image
|
||||
* @this: Protocol instance
|
||||
* @image_index: Image index number
|
||||
* @image: New image
|
||||
* @image_size: Size of new image
|
||||
* @vendor_code: Vendor-specific update policy
|
||||
* @progress: Function to report the progress of update
|
||||
* @abort_reason: Pointer to string of abort reason
|
||||
*
|
||||
* Update the firmware to new image, using dfu. The new image should
|
||||
* have FIT image format commonly used in U-Boot.
|
||||
* @vendor_code, @progress and @abort_reason are not supported.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_fit_set_image(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
const void *image,
|
||||
efi_uintn_t image_size,
|
||||
const void *vendor_code,
|
||||
efi_status_t (*progress)(efi_uintn_t completion),
|
||||
u16 **abort_reason)
|
||||
{
|
||||
EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
|
||||
image_size, vendor_code, progress, abort_reason);
|
||||
|
||||
if (!image || image_index != 1)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
if (fit_update(image))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
const struct efi_firmware_management_protocol efi_fmp_fit = {
|
||||
.get_image_info = efi_firmware_fit_get_image_info,
|
||||
.get_image = efi_firmware_get_image_unsupported,
|
||||
.set_image = efi_firmware_fit_set_image,
|
||||
.check_image = efi_firmware_check_image_unsupported,
|
||||
.get_package_info = efi_firmware_get_package_info_unsupported,
|
||||
.set_package_info = efi_firmware_set_package_info_unsupported,
|
||||
};
|
||||
#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */
|
||||
|
||||
#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW
|
||||
/*
|
||||
* This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
|
||||
* method with raw data.
|
||||
*/
|
||||
const efi_guid_t efi_firmware_image_type_uboot_raw =
|
||||
EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
|
||||
|
||||
/**
|
||||
* efi_firmware_raw_get_image_info - return information about the current
|
||||
firmware image
|
||||
* @this: Protocol instance
|
||||
* @image_info_size: Size of @image_info
|
||||
* @image_info: Image information
|
||||
* @descriptor_version: Pointer to version number
|
||||
* @descriptor_count: Pointer to number of descriptors
|
||||
* @descriptor_size: Pointer to descriptor size
|
||||
* package_version: Package version
|
||||
* package_version_name: Package version's name
|
||||
*
|
||||
* Return information bout the current firmware image in @image_info.
|
||||
* @image_info will consist of a number of descriptors.
|
||||
* Each descriptor will be created based on "dfu_alt_info" variable.
|
||||
*
|
||||
* Return status code
|
||||
*/
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_raw_get_image_info(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
efi_uintn_t *image_info_size,
|
||||
struct efi_firmware_image_descriptor *image_info,
|
||||
u32 *descriptor_version,
|
||||
u8 *descriptor_count,
|
||||
efi_uintn_t *descriptor_size,
|
||||
u32 *package_version,
|
||||
u16 **package_version_name)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
|
||||
image_info_size, image_info,
|
||||
descriptor_version, descriptor_count, descriptor_size,
|
||||
package_version, package_version_name);
|
||||
|
||||
if (!image_info_size)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
if (*image_info_size &&
|
||||
(!image_info || !descriptor_version || !descriptor_count ||
|
||||
!descriptor_size || !package_version || !package_version_name))
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
ret = efi_get_dfu_info(image_info_size, image_info,
|
||||
descriptor_version, descriptor_count,
|
||||
descriptor_size,
|
||||
package_version, package_version_name,
|
||||
&efi_firmware_image_type_uboot_raw);
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_firmware_raw_set_image - update the firmware image
|
||||
* @this: Protocol instance
|
||||
* @image_index: Image index number
|
||||
* @image: New image
|
||||
* @image_size: Size of new image
|
||||
* @vendor_code: Vendor-specific update policy
|
||||
* @progress: Function to report the progress of update
|
||||
* @abort_reason: Pointer to string of abort reason
|
||||
*
|
||||
* Update the firmware to new image, using dfu. The new image should
|
||||
* be a single raw image.
|
||||
* @vendor_code, @progress and @abort_reason are not supported.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static
|
||||
efi_status_t EFIAPI efi_firmware_raw_set_image(
|
||||
struct efi_firmware_management_protocol *this,
|
||||
u8 image_index,
|
||||
const void *image,
|
||||
efi_uintn_t image_size,
|
||||
const void *vendor_code,
|
||||
efi_status_t (*progress)(efi_uintn_t completion),
|
||||
u16 **abort_reason)
|
||||
{
|
||||
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);
|
||||
|
||||
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
|
||||
NULL, NULL))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
const struct efi_firmware_management_protocol efi_fmp_raw = {
|
||||
.get_image_info = efi_firmware_raw_get_image_info,
|
||||
.get_image = efi_firmware_get_image_unsupported,
|
||||
.set_image = efi_firmware_raw_set_image,
|
||||
.check_image = efi_firmware_check_image_unsupported,
|
||||
.get_package_info = efi_firmware_get_package_info_unsupported,
|
||||
.set_package_info = efi_firmware_set_package_info_unsupported,
|
||||
};
|
||||
#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */
|
||||
@@ -133,6 +133,10 @@ efi_status_t efi_init_runtime_supported(void)
|
||||
#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
|
||||
rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE))
|
||||
rt_table->runtime_services_supported |=
|
||||
(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
|
||||
EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
|
||||
|
||||
ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
|
||||
rt_table);
|
||||
@@ -448,6 +452,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_update_capsule_unsupported() - process information from operating system
|
||||
*
|
||||
* This function implements the UpdateCapsule() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @scatter_gather_list: pointer to array of physical pointers
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 scatter_gather_list)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_query_capsule_caps_unsupported() - check if capsule is supported
|
||||
*
|
||||
* This function implements the QueryCapsuleCapabilities() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @maximum_capsule_size: maximum capsule size
|
||||
* @reset_type: type of reset needed for capsule update
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 *maximum_capsule_size,
|
||||
u32 *reset_type)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_is_runtime_service_pointer() - check if pointer points to runtime table
|
||||
*
|
||||
@@ -471,6 +519,13 @@ void efi_runtime_detach(void)
|
||||
efi_runtime_services.reset_system = efi_reset_system;
|
||||
efi_runtime_services.get_time = efi_get_time;
|
||||
efi_runtime_services.set_time = efi_set_time;
|
||||
if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) {
|
||||
/* won't support at runtime */
|
||||
efi_runtime_services.update_capsule =
|
||||
efi_update_capsule_unsupported;
|
||||
efi_runtime_services.query_capsule_caps =
|
||||
efi_query_capsule_caps_unsupported;
|
||||
}
|
||||
|
||||
/* Update CRC32 */
|
||||
efi_update_table_header_crc32(&efi_runtime_services.hdr);
|
||||
@@ -879,50 +934,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_update_capsule() - process information from operating system
|
||||
*
|
||||
* This function implements the UpdateCapsule() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @scatter_gather_list: pointer to arry of physical pointers
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_update_capsule(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 scatter_gather_list)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_query_capsule_caps() - check if capsule is supported
|
||||
*
|
||||
* This function implements the QueryCapsuleCapabilities() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @maximum_capsule_size: maximum capsule size
|
||||
* @reset_type: type of reset needed for capsule update
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 *maximum_capsule_size,
|
||||
u32 *reset_type)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
|
||||
.hdr = {
|
||||
.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
|
||||
@@ -940,7 +951,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
|
||||
.set_variable = efi_set_variable,
|
||||
.get_next_high_mono_count = (void *)&efi_unimplemented,
|
||||
.reset_system = &efi_reset_system_boottime,
|
||||
#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
|
||||
.update_capsule = efi_update_capsule,
|
||||
.query_capsule_caps = efi_query_capsule_caps,
|
||||
#else
|
||||
.update_capsule = efi_update_capsule_unsupported,
|
||||
.query_capsule_caps = efi_query_capsule_caps_unsupported,
|
||||
#endif
|
||||
.query_variable_info = efi_query_variable_info,
|
||||
};
|
||||
|
||||
@@ -100,9 +100,9 @@ static efi_status_t efi_init_secure_boot(void)
|
||||
|
||||
ret = efi_set_variable_int(L"SignatureSupport",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_READ_ONLY |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS |
|
||||
EFI_VARIABLE_READ_ONLY,
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
sizeof(signature_types),
|
||||
&signature_types, false);
|
||||
if (ret != EFI_SUCCESS)
|
||||
@@ -117,6 +117,61 @@ static efi_status_t efi_init_secure_boot(void)
|
||||
}
|
||||
#endif /* CONFIG_EFI_SECURE_BOOT */
|
||||
|
||||
/**
|
||||
* efi_init_capsule - initialize capsule update state
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_init_capsule(void)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) {
|
||||
ret = efi_set_variable_int(L"CapsuleMax",
|
||||
&efi_guid_capsule_report,
|
||||
EFI_VARIABLE_READ_ONLY |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
22, L"CapsuleFFFF", false);
|
||||
if (ret != EFI_SUCCESS)
|
||||
printf("EFI: cannot initialize CapsuleMax variable\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_init_os_indications() - indicate supported features for OS requests
|
||||
*
|
||||
* Set the OsIndicationsSupported variable.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_init_os_indications(void)
|
||||
{
|
||||
u64 os_indications_supported = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT))
|
||||
os_indications_supported |=
|
||||
EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK))
|
||||
os_indications_supported |=
|
||||
EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT))
|
||||
os_indications_supported |=
|
||||
EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
|
||||
|
||||
return efi_set_variable_int(L"OsIndicationsSupported",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS |
|
||||
EFI_VARIABLE_READ_ONLY,
|
||||
sizeof(os_indications_supported),
|
||||
&os_indications_supported, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_init_obj_list() - Initialize and populate EFI object list
|
||||
*
|
||||
@@ -124,7 +179,6 @@ static efi_status_t efi_init_secure_boot(void)
|
||||
*/
|
||||
efi_status_t efi_init_obj_list(void)
|
||||
{
|
||||
u64 os_indications_supported = 0; /* None */
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
/* Initialize once only */
|
||||
@@ -157,12 +211,6 @@ efi_status_t efi_init_obj_list(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
|
||||
ret = efi_tcg2_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Initialize variable services */
|
||||
ret = efi_init_variables();
|
||||
if (ret != EFI_SUCCESS)
|
||||
@@ -174,13 +222,7 @@ efi_status_t efi_init_obj_list(void)
|
||||
goto out;
|
||||
|
||||
/* Indicate supported features */
|
||||
ret = efi_set_variable_int(L"OsIndicationsSupported",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS |
|
||||
EFI_VARIABLE_READ_ONLY,
|
||||
sizeof(os_indications_supported),
|
||||
&os_indications_supported, false);
|
||||
ret = efi_init_os_indications();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
@@ -189,6 +231,12 @@ efi_status_t efi_init_obj_list(void)
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
|
||||
ret = efi_tcg2_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Secure boot */
|
||||
ret = efi_init_secure_boot();
|
||||
if (ret != EFI_SUCCESS)
|
||||
@@ -233,11 +281,19 @@ efi_status_t efi_init_obj_list(void)
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = efi_init_capsule();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Initialize EFI runtime services */
|
||||
ret = efi_reset_system_init();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Execute capsules after reboot */
|
||||
if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) &&
|
||||
!IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
|
||||
ret = efi_launch_capsules();
|
||||
out:
|
||||
efi_obj_list_initialized = ret;
|
||||
return ret;
|
||||
|
||||
@@ -14,11 +14,24 @@
|
||||
#include <efi_tcg2.h>
|
||||
#include <log.h>
|
||||
#include <tpm-v2.h>
|
||||
#include <u-boot/sha1.h>
|
||||
#include <u-boot/sha256.h>
|
||||
#include <u-boot/sha512.h>
|
||||
#include <linux/unaligned/access_ok.h>
|
||||
#include <linux/unaligned/generic.h>
|
||||
#include <hexdump.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
struct event_log_buffer {
|
||||
void *buffer;
|
||||
void *final_buffer;
|
||||
size_t pos; /* eventlog position */
|
||||
size_t final_pos; /* final events config table position */
|
||||
size_t last_event_size;
|
||||
bool get_event_called;
|
||||
bool truncated;
|
||||
};
|
||||
|
||||
static struct event_log_buffer event_log;
|
||||
/*
|
||||
* When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
|
||||
* Since the current tpm2_get_capability() response buffers starts at
|
||||
@@ -30,33 +43,40 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||
#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
|
||||
offsetof(struct tpms_tagged_property, value))
|
||||
|
||||
struct {
|
||||
static const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID;
|
||||
static const efi_guid_t efi_guid_final_events = EFI_TCG2_FINAL_EVENTS_TABLE_GUID;
|
||||
|
||||
struct digest_info {
|
||||
u16 hash_alg;
|
||||
u32 hash_mask;
|
||||
} hash_algo_list[] = {
|
||||
u16 hash_len;
|
||||
};
|
||||
|
||||
const static struct digest_info hash_algo_list[] = {
|
||||
{
|
||||
TPM2_ALG_SHA1,
|
||||
EFI_TCG2_BOOT_HASH_ALG_SHA1,
|
||||
TPM2_SHA1_DIGEST_SIZE,
|
||||
},
|
||||
{
|
||||
TPM2_ALG_SHA256,
|
||||
EFI_TCG2_BOOT_HASH_ALG_SHA256,
|
||||
TPM2_SHA256_DIGEST_SIZE,
|
||||
},
|
||||
{
|
||||
TPM2_ALG_SHA384,
|
||||
EFI_TCG2_BOOT_HASH_ALG_SHA384,
|
||||
TPM2_SHA384_DIGEST_SIZE,
|
||||
},
|
||||
{
|
||||
TPM2_ALG_SHA512,
|
||||
EFI_TCG2_BOOT_HASH_ALG_SHA512,
|
||||
},
|
||||
{
|
||||
TPM2_ALG_SM3_256,
|
||||
EFI_TCG2_BOOT_HASH_ALG_SM3_256,
|
||||
TPM2_SHA512_DIGEST_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
#define MAX_HASH_COUNT ARRAY_SIZE(hash_algo_list)
|
||||
|
||||
/**
|
||||
* alg_to_mask - Get a TCG hash mask for algorithms
|
||||
*
|
||||
@@ -76,7 +96,146 @@ static u32 alg_to_mask(u16 hash_alg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID;
|
||||
/**
|
||||
* alg_to_len - Get a TCG hash len for algorithms
|
||||
*
|
||||
* @hash_alg: TCG defined algorithm
|
||||
*
|
||||
* @Return: len of chosen algorithm, 0 if the algorithm is not supported
|
||||
*/
|
||||
static u16 alg_to_len(u16 hash_alg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_HASH_COUNT; i++) {
|
||||
if (hash_algo_list[i].hash_alg == hash_alg)
|
||||
return hash_algo_list[i].hash_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 tcg_event_final_size(struct tpml_digest_values *digest_list)
|
||||
{
|
||||
u32 len;
|
||||
int i;
|
||||
|
||||
len = offsetof(struct tcg_pcr_event2, digests);
|
||||
len += offsetof(struct tpml_digest_values, digests);
|
||||
for (i = 0; i < digest_list->count; i++) {
|
||||
u16 hash_alg = digest_list->digests[i].hash_alg;
|
||||
|
||||
len += offsetof(struct tpmt_ha, digest);
|
||||
len += alg_to_len(hash_alg);
|
||||
}
|
||||
len += sizeof(u32); /* tcg_pcr_event2 event_size*/
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* tcg2_pcr_extend - Extend PCRs for a TPM2 device for a given tpml_digest_values
|
||||
*
|
||||
* @dev: device
|
||||
* @digest_list: list of digest algorithms to extend
|
||||
*
|
||||
* @Return: status code
|
||||
*/
|
||||
static efi_status_t tcg2_pcr_extend(struct udevice *dev, u32 pcr_index,
|
||||
struct tpml_digest_values *digest_list)
|
||||
{
|
||||
u32 rc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < digest_list->count; i++) {
|
||||
u32 alg = digest_list->digests[i].hash_alg;
|
||||
|
||||
rc = tpm2_pcr_extend(dev, pcr_index, alg,
|
||||
(u8 *)&digest_list->digests[i].digest,
|
||||
alg_to_len(alg));
|
||||
if (rc) {
|
||||
EFI_PRINT("Failed to extend PCR\n");
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* tcg2_agile_log_append - Append an agile event to out eventlog
|
||||
*
|
||||
* @pcr_index: PCR index
|
||||
* @event_type: type of event added
|
||||
* @digest_list: list of digest algorithms to add
|
||||
* @size: size of event
|
||||
* @event: event to add
|
||||
*
|
||||
* @Return: status code
|
||||
*/
|
||||
static efi_status_t tcg2_agile_log_append(u32 pcr_index, u32 event_type,
|
||||
struct tpml_digest_values *digest_list,
|
||||
u32 size, u8 event[])
|
||||
{
|
||||
void *log = event_log.buffer + event_log.pos;
|
||||
size_t pos;
|
||||
int i;
|
||||
u32 event_size;
|
||||
|
||||
if (event_log.get_event_called)
|
||||
log = event_log.final_buffer + event_log.final_pos;
|
||||
|
||||
/*
|
||||
* size refers to the length of event[] only, we need to check against
|
||||
* the final tcg_pcr_event2 size
|
||||
*/
|
||||
event_size = size + tcg_event_final_size(digest_list);
|
||||
if (event_log.pos + event_size > TPM2_EVENT_LOG_SIZE ||
|
||||
event_log.final_pos + event_size > TPM2_EVENT_LOG_SIZE) {
|
||||
event_log.truncated = true;
|
||||
return EFI_VOLUME_FULL;
|
||||
}
|
||||
|
||||
put_unaligned_le32(pcr_index, log);
|
||||
pos = offsetof(struct tcg_pcr_event2, event_type);
|
||||
put_unaligned_le32(event_type, log + pos);
|
||||
pos = offsetof(struct tcg_pcr_event2, digests); /* count */
|
||||
put_unaligned_le32(digest_list->count, log + pos);
|
||||
|
||||
pos += offsetof(struct tpml_digest_values, digests);
|
||||
for (i = 0; i < digest_list->count; i++) {
|
||||
u16 hash_alg = digest_list->digests[i].hash_alg;
|
||||
u8 *digest = (u8 *)&digest_list->digests[i].digest;
|
||||
|
||||
put_unaligned_le16(hash_alg, log + pos);
|
||||
pos += offsetof(struct tpmt_ha, digest);
|
||||
memcpy(log + pos, digest, alg_to_len(hash_alg));
|
||||
pos += alg_to_len(hash_alg);
|
||||
}
|
||||
|
||||
put_unaligned_le32(size, log + pos);
|
||||
pos += sizeof(u32); /* tcg_pcr_event2 event_size*/
|
||||
memcpy(log + pos, event, size);
|
||||
pos += size;
|
||||
|
||||
/* make sure the calculated buffer is what we checked against */
|
||||
if (pos != event_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/* if GetEventLog hasn't been called update the normal log */
|
||||
if (!event_log.get_event_called) {
|
||||
event_log.pos += pos;
|
||||
event_log.last_event_size = pos;
|
||||
} else {
|
||||
/* if GetEventLog has been called update config table log */
|
||||
struct efi_tcg2_final_events_table *final_event;
|
||||
|
||||
final_event =
|
||||
(struct efi_tcg2_final_events_table *)(event_log.final_buffer);
|
||||
final_event->number_of_events++;
|
||||
event_log.final_pos += pos;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* platform_get_tpm_device() - retrieve TPM device
|
||||
@@ -208,7 +367,7 @@ static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
|
||||
*
|
||||
* Return: true if PCR is active
|
||||
*/
|
||||
bool is_active_pcr(struct tpms_pcr_selection *selection)
|
||||
static bool is_active_pcr(struct tpms_pcr_selection *selection)
|
||||
{
|
||||
int i;
|
||||
/*
|
||||
@@ -308,6 +467,103 @@ out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_active_pcr_banks() - returns the currently active PCR banks
|
||||
*
|
||||
* @active_pcr_banks: pointer for receiving the bitmap of currently
|
||||
* active PCR banks
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t __get_active_pcr_banks(u32 *active_pcr_banks)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 active, supported, pcr_banks;
|
||||
efi_status_t ret;
|
||||
int err;
|
||||
|
||||
ret = platform_get_tpm2_device(&dev);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
err = tpm2_get_pcr_info(dev, &supported, &active, &pcr_banks);
|
||||
if (err) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*active_pcr_banks = active;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* tcg2_create_digest - create a list of digests of the supported PCR banks
|
||||
* for a given memory range
|
||||
*
|
||||
* @input: input memory
|
||||
* @length: length of buffer to calculate the digest
|
||||
* @digest_list: list of digests to fill in
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t tcg2_create_digest(const u8 *input, u32 length,
|
||||
struct tpml_digest_values *digest_list)
|
||||
{
|
||||
sha1_context ctx;
|
||||
sha256_context ctx_256;
|
||||
sha512_context ctx_512;
|
||||
u8 final[TPM2_ALG_SHA512];
|
||||
efi_status_t ret;
|
||||
u32 active;
|
||||
int i;
|
||||
|
||||
ret = __get_active_pcr_banks(&active);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
digest_list->count = 0;
|
||||
for (i = 0; i < MAX_HASH_COUNT; i++) {
|
||||
u16 hash_alg = hash_algo_list[i].hash_alg;
|
||||
|
||||
if (!(active & alg_to_mask(hash_alg)))
|
||||
continue;
|
||||
switch (hash_alg) {
|
||||
case TPM2_ALG_SHA1:
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, input, length);
|
||||
sha1_finish(&ctx, final);
|
||||
digest_list->count++;
|
||||
break;
|
||||
case TPM2_ALG_SHA256:
|
||||
sha256_starts(&ctx_256);
|
||||
sha256_update(&ctx_256, input, length);
|
||||
sha256_finish(&ctx_256, final);
|
||||
digest_list->count++;
|
||||
break;
|
||||
case TPM2_ALG_SHA384:
|
||||
sha384_starts(&ctx_512);
|
||||
sha384_update(&ctx_512, input, length);
|
||||
sha384_finish(&ctx_512, final);
|
||||
digest_list->count++;
|
||||
break;
|
||||
case TPM2_ALG_SHA512:
|
||||
sha512_starts(&ctx_512);
|
||||
sha512_update(&ctx_512, input, length);
|
||||
sha512_finish(&ctx_512, final);
|
||||
digest_list->count++;
|
||||
break;
|
||||
default:
|
||||
EFI_PRINT("Unsupported algorithm %x\n", hash_alg);
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
digest_list->digests[i].hash_alg = hash_alg;
|
||||
memcpy(&digest_list->digests[i].digest, final, (u32)alg_to_len(hash_alg));
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_tcg2_get_capability() - protocol capability information and state information
|
||||
*
|
||||
@@ -427,7 +683,28 @@ efi_tcg2_get_eventlog(struct efi_tcg2_protocol *this,
|
||||
u64 *event_log_location, u64 *event_log_last_entry,
|
||||
bool *event_log_truncated)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
struct udevice *dev;
|
||||
|
||||
EFI_ENTRY("%p, %u, %p, %p, %p", this, log_format, event_log_location,
|
||||
event_log_last_entry, event_log_truncated);
|
||||
|
||||
ret = platform_get_tpm2_device(&dev);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
event_log_location = NULL;
|
||||
event_log_last_entry = NULL;
|
||||
*event_log_truncated = false;
|
||||
ret = EFI_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
*event_log_location = (uintptr_t)event_log.buffer;
|
||||
*event_log_last_entry = (uintptr_t)(event_log.buffer + event_log.pos -
|
||||
event_log.last_event_size);
|
||||
*event_log_truncated = event_log.truncated;
|
||||
event_log.get_event_called = true;
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +727,76 @@ efi_tcg2_hash_log_extend_event(struct efi_tcg2_protocol *this, u64 flags,
|
||||
u64 data_to_hash, u64 data_to_hash_len,
|
||||
struct efi_tcg2_event *efi_tcg_event)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
struct udevice *dev;
|
||||
efi_status_t ret;
|
||||
u32 event_type, pcr_index, event_size;
|
||||
struct tpml_digest_values digest_list;
|
||||
|
||||
EFI_ENTRY("%p, %llu, %llu, %llu, %p", this, flags, data_to_hash,
|
||||
data_to_hash_len, efi_tcg_event);
|
||||
|
||||
if (!this || !data_to_hash || !efi_tcg_event) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = platform_get_tpm2_device(&dev);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
if (efi_tcg_event->size < efi_tcg_event->header.header_size +
|
||||
sizeof(u32)) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (efi_tcg_event->header.pcr_index < 0 ||
|
||||
efi_tcg_event->header.pcr_index > TPM2_MAX_PCRS) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* if PE_COFF_IMAGE is set we need to make sure the image is not
|
||||
* corrupted, verify it and hash the PE/COFF image in accordance with
|
||||
* the procedure specified in "Calculating the PE Image Hash"
|
||||
* section of the "Windows Authenticode Portable Executable Signature
|
||||
* Format"
|
||||
* Not supported for now
|
||||
*/
|
||||
if (flags & PE_COFF_IMAGE) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pcr_index = efi_tcg_event->header.pcr_index;
|
||||
event_type = efi_tcg_event->header.event_type;
|
||||
|
||||
ret = tcg2_create_digest((u8 *)data_to_hash, data_to_hash_len,
|
||||
&digest_list);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
if (flags & EFI_TCG2_EXTEND_ONLY) {
|
||||
if (event_log.truncated)
|
||||
ret = EFI_VOLUME_FULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The efi_tcg_event size includes the size component and the
|
||||
* headersize
|
||||
*/
|
||||
event_size = efi_tcg_event->size - sizeof(efi_tcg_event->size) -
|
||||
efi_tcg_event->header.header_size;
|
||||
ret = tcg2_agile_log_append(pcr_index, event_type, &digest_list,
|
||||
event_size, efi_tcg_event->event);
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,7 +810,7 @@ efi_tcg2_hash_log_extend_event(struct efi_tcg2_protocol *this, u64 flags,
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI
|
||||
static efi_status_t EFIAPI
|
||||
efi_tcg2_submit_command(struct efi_tcg2_protocol *this,
|
||||
u32 input_param_block_size, u8 *input_param_block,
|
||||
u32 output_param_block_size, u8 *output_param_block)
|
||||
@@ -481,11 +827,16 @@ efi_tcg2_submit_command(struct efi_tcg2_protocol *this,
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI
|
||||
static efi_status_t EFIAPI
|
||||
efi_tcg2_get_active_pcr_banks(struct efi_tcg2_protocol *this,
|
||||
u32 *active_pcr_banks)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p, %p", this, active_pcr_banks);
|
||||
ret = __get_active_pcr_banks(active_pcr_banks);
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -496,7 +847,7 @@ efi_tcg2_get_active_pcr_banks(struct efi_tcg2_protocol *this,
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI
|
||||
static efi_status_t EFIAPI
|
||||
efi_tcg2_set_active_pcr_banks(struct efi_tcg2_protocol *this,
|
||||
u32 active_pcr_banks)
|
||||
{
|
||||
@@ -515,7 +866,7 @@ efi_tcg2_set_active_pcr_banks(struct efi_tcg2_protocol *this,
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI
|
||||
static efi_status_t EFIAPI
|
||||
efi_tcg2_get_result_of_set_active_pcr_banks(struct efi_tcg2_protocol *this,
|
||||
u32 *operation_present, u32 *response)
|
||||
{
|
||||
@@ -532,6 +883,169 @@ static const struct efi_tcg2_protocol efi_tcg2_protocol = {
|
||||
.get_result_of_set_active_pcr_banks = efi_tcg2_get_result_of_set_active_pcr_banks,
|
||||
};
|
||||
|
||||
/**
|
||||
* create_specid_event() - Create the first event in the eventlog
|
||||
*
|
||||
* @dev: tpm device
|
||||
* @event_header: Pointer to the final event header
|
||||
* @event_size: final spec event size
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t create_specid_event(struct udevice *dev, void *buffer,
|
||||
size_t *event_size)
|
||||
{
|
||||
struct tcg_efi_spec_id_event *spec_event;
|
||||
size_t spec_event_size;
|
||||
efi_status_t ret = EFI_DEVICE_ERROR;
|
||||
u32 active, supported;
|
||||
int err, i;
|
||||
|
||||
/*
|
||||
* Create Spec event. This needs to be the first event in the log
|
||||
* according to the TCG EFI protocol spec
|
||||
*/
|
||||
|
||||
/* Setup specID event data */
|
||||
spec_event = (struct tcg_efi_spec_id_event *)buffer;
|
||||
memcpy(spec_event->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
|
||||
sizeof(spec_event->signature));
|
||||
put_unaligned_le32(0, &spec_event->platform_class); /* type client */
|
||||
spec_event->spec_version_minor =
|
||||
TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2;
|
||||
spec_event->spec_version_major =
|
||||
TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2;
|
||||
spec_event->spec_errata =
|
||||
TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2;
|
||||
spec_event->uintn_size = sizeof(efi_uintn_t) / sizeof(u32);
|
||||
|
||||
err = tpm2_get_pcr_info(dev, &supported, &active,
|
||||
&spec_event->number_of_algorithms);
|
||||
if (err)
|
||||
goto out;
|
||||
if (spec_event->number_of_algorithms > MAX_HASH_COUNT ||
|
||||
spec_event->number_of_algorithms < 1)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < spec_event->number_of_algorithms; i++) {
|
||||
u16 hash_alg = hash_algo_list[i].hash_alg;
|
||||
u16 hash_len = hash_algo_list[i].hash_len;
|
||||
|
||||
if (active && alg_to_mask(hash_alg)) {
|
||||
put_unaligned_le16(hash_alg,
|
||||
&spec_event->digest_sizes[i].algorithm_id);
|
||||
put_unaligned_le16(hash_len,
|
||||
&spec_event->digest_sizes[i].digest_size);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* the size of the spec event and placement of vendor_info_size
|
||||
* depends on supported algoriths
|
||||
*/
|
||||
spec_event_size =
|
||||
offsetof(struct tcg_efi_spec_id_event, digest_sizes) +
|
||||
spec_event->number_of_algorithms * sizeof(spec_event->digest_sizes[0]);
|
||||
/* no vendor info for us */
|
||||
memset(buffer + spec_event_size, 0,
|
||||
sizeof(spec_event->vendor_info_size));
|
||||
spec_event_size += sizeof(spec_event->vendor_info_size);
|
||||
*event_size = spec_event_size;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_final_event() - Create the final event and install the config
|
||||
* defined by the TCG EFI spec
|
||||
*/
|
||||
static efi_status_t create_final_event(void)
|
||||
{
|
||||
struct efi_tcg2_final_events_table *final_event;
|
||||
efi_status_t ret;
|
||||
|
||||
/*
|
||||
* All events generated after the invocation of
|
||||
* EFI_TCG2_GET_EVENT_LOGS need to be stored in an instance of an
|
||||
* EFI_CONFIGURATION_TABLE
|
||||
*/
|
||||
ret = efi_allocate_pool(EFI_ACPI_MEMORY_NVS, TPM2_EVENT_LOG_SIZE,
|
||||
&event_log.final_buffer);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
memset(event_log.final_buffer, 0xff, TPM2_EVENT_LOG_SIZE);
|
||||
final_event = event_log.final_buffer;
|
||||
final_event->number_of_events = 0;
|
||||
final_event->version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION;
|
||||
event_log.final_pos = sizeof(*final_event);
|
||||
ret = efi_install_configuration_table(&efi_guid_final_events,
|
||||
final_event);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_init_event_log() - initialize an eventlog
|
||||
*/
|
||||
static efi_status_t efi_init_event_log(void)
|
||||
{
|
||||
/*
|
||||
* vendor_info_size is currently set to 0, we need to change the length
|
||||
* and allocate the flexible array member if this changes
|
||||
*/
|
||||
struct tcg_pcr_event *event_header = NULL;
|
||||
struct udevice *dev;
|
||||
size_t spec_event_size;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = platform_get_tpm2_device(&dev);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, TPM2_EVENT_LOG_SIZE,
|
||||
(void **)&event_log.buffer);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* initialize log area as 0xff so the OS can easily figure out the
|
||||
* last log entry
|
||||
*/
|
||||
memset(event_log.buffer, 0xff, TPM2_EVENT_LOG_SIZE);
|
||||
event_log.pos = 0;
|
||||
event_log.last_event_size = 0;
|
||||
event_log.get_event_called = false;
|
||||
event_log.truncated = false;
|
||||
|
||||
/*
|
||||
* The log header is defined to be in SHA1 event log entry format.
|
||||
* Setup event header
|
||||
*/
|
||||
event_header = (struct tcg_pcr_event *)event_log.buffer;
|
||||
put_unaligned_le32(0, &event_header->pcr_index);
|
||||
put_unaligned_le32(EV_NO_ACTION, &event_header->event_type);
|
||||
memset(&event_header->digest, 0, sizeof(event_header->digest));
|
||||
ret = create_specid_event(dev, event_log.buffer + sizeof(*event_header),
|
||||
&spec_event_size);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
put_unaligned_le32(spec_event_size, &event_header->event_size);
|
||||
event_log.pos = spec_event_size + sizeof(*event_header);
|
||||
event_log.last_event_size = event_log.pos;
|
||||
|
||||
ret = create_final_event();
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_tcg2_register() - register EFI_TCG2_PROTOCOL
|
||||
*
|
||||
@@ -549,6 +1063,11 @@ efi_status_t efi_tcg2_register(void)
|
||||
log_warning("Unable to find TPMv2 device\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
ret = efi_init_event_log();
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
|
||||
(void *)&efi_tcg2_protocol);
|
||||
if (ret != EFI_SUCCESS)
|
||||
|
||||
11
lib/tpm-v2.c
11
lib/tpm-v2.c
@@ -80,11 +80,12 @@ u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
|
||||
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
|
||||
}
|
||||
|
||||
u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)
|
||||
u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
|
||||
const u8 *digest, u32 digest_len)
|
||||
{
|
||||
u8 command_v2[COMMAND_BUFFER_SIZE] = {
|
||||
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
|
||||
tpm_u32(33 + TPM2_DIGEST_LEN), /* Length */
|
||||
tpm_u32(33 + digest_len), /* Length */
|
||||
tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
|
||||
|
||||
/* HANDLE */
|
||||
@@ -99,7 +100,7 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)
|
||||
tpm_u16(0), /* Size of <hmac/password> */
|
||||
/* <hmac/password> (if any) */
|
||||
tpm_u32(1), /* Count (number of hashes) */
|
||||
tpm_u16(TPM2_ALG_SHA256), /* Algorithm of the hash */
|
||||
tpm_u16(algorithm), /* Algorithm of the hash */
|
||||
/* STRING(digest) Digest */
|
||||
};
|
||||
unsigned int offset = 33;
|
||||
@@ -110,8 +111,8 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)
|
||||
* - the digest
|
||||
*/
|
||||
ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
|
||||
offset, digest, TPM2_DIGEST_LEN);
|
||||
offset += TPM2_DIGEST_LEN;
|
||||
offset, digest, digest_len);
|
||||
offset += digest_len;
|
||||
if (ret)
|
||||
return TPM_LIB_ERROR;
|
||||
|
||||
|
||||
5
test/py/tests/test_efi_capsule/capsule_defs.py
Normal file
5
test/py/tests/test_efi_capsule/capsule_defs.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
# Directories
|
||||
CAPSULE_DATA_DIR = '/EFI/CapsuleTestData'
|
||||
CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
|
||||
74
test/py/tests/test_efi_capsule/conftest.py
Normal file
74
test/py/tests/test_efi_capsule/conftest.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Copyright (c) 2020, Linaro Limited
|
||||
# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
from subprocess import call, check_call, check_output, CalledProcessError
|
||||
import pytest
|
||||
from capsule_defs import *
|
||||
|
||||
#
|
||||
# Fixture for UEFI secure boot test
|
||||
#
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def efi_capsule_data(request, u_boot_config):
|
||||
"""Set up a file system to be used in UEFI capsule test.
|
||||
|
||||
Args:
|
||||
request: Pytest request object.
|
||||
u_boot_config: U-boot configuration.
|
||||
|
||||
Return:
|
||||
A path to disk image to be used for testing
|
||||
"""
|
||||
global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
|
||||
|
||||
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
|
||||
data_dir = mnt_point + CAPSULE_DATA_DIR
|
||||
install_dir = mnt_point + CAPSULE_INSTALL_DIR
|
||||
image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
|
||||
|
||||
try:
|
||||
# Create a target device
|
||||
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
|
||||
|
||||
check_call('rm -rf %s' % mnt_point, shell=True)
|
||||
check_call('mkdir -p %s' % data_dir, shell=True)
|
||||
check_call('mkdir -p %s' % install_dir, shell=True)
|
||||
|
||||
# Create capsule files
|
||||
# two regions: one for u-boot.bin and the other for u-boot.env
|
||||
check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old -> u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
|
||||
shell=True)
|
||||
check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' %
|
||||
(u_boot_config.source_dir, data_dir),
|
||||
shell=True)
|
||||
check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' %
|
||||
(data_dir, u_boot_config.build_dir),
|
||||
shell=True)
|
||||
check_call('cd %s; %s/tools/mkeficapsule --fit uboot_bin_env.itb --index 1 Test01' %
|
||||
(data_dir, u_boot_config.build_dir),
|
||||
shell=True)
|
||||
check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 Test02' %
|
||||
(data_dir, u_boot_config.build_dir),
|
||||
shell=True)
|
||||
|
||||
# Create a disk image with EFI system partition
|
||||
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
|
||||
(mnt_point, image_path), shell=True)
|
||||
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
|
||||
image_path, shell=True)
|
||||
|
||||
except CalledProcessError as exception:
|
||||
pytest.skip('Setup failed: %s' % exception.cmd)
|
||||
return
|
||||
else:
|
||||
yield image_path
|
||||
finally:
|
||||
call('rm -rf %s' % mnt_point, shell=True)
|
||||
call('rm -f %s' % image_path, shell=True)
|
||||
call('rm -f ./spi.bin', shell=True)
|
||||
241
test/py/tests/test_efi_capsule/test_capsule_firmware.py
Normal file
241
test/py/tests/test_efi_capsule/test_capsule_firmware.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Copyright (c) 2020, Linaro Limited
|
||||
# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
|
||||
#
|
||||
# U-Boot UEFI: Firmware Update Test
|
||||
|
||||
"""
|
||||
This test verifies capsule-on-disk firmware update
|
||||
"""
|
||||
|
||||
from subprocess import check_call, check_output, CalledProcessError
|
||||
import pytest
|
||||
from capsule_defs import *
|
||||
|
||||
|
||||
@pytest.mark.boardspec('sandbox')
|
||||
@pytest.mark.buildconfigspec('efi_capsule_firmware_fit')
|
||||
@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
|
||||
@pytest.mark.buildconfigspec('efi_capsule_on_disk')
|
||||
@pytest.mark.buildconfigspec('dfu')
|
||||
@pytest.mark.buildconfigspec('dfu_sf')
|
||||
@pytest.mark.buildconfigspec('cmd_efidebug')
|
||||
@pytest.mark.buildconfigspec('cmd_fat')
|
||||
@pytest.mark.buildconfigspec('cmd_memory')
|
||||
@pytest.mark.buildconfigspec('cmd_nvedit_efi')
|
||||
@pytest.mark.buildconfigspec('cmd_sf')
|
||||
@pytest.mark.slow
|
||||
class TestEfiCapsuleFirmwareFit(object):
|
||||
def test_efi_capsule_fw1(
|
||||
self, u_boot_config, u_boot_console, efi_capsule_data):
|
||||
"""
|
||||
Test Case 1 - Update U-Boot and U-Boot environment on SPI Flash
|
||||
but with OsIndications unset
|
||||
No update should happen
|
||||
0x100000-0x150000: U-Boot binary (but dummy)
|
||||
0x150000-0x200000: U-Boot environment (but dummy)
|
||||
"""
|
||||
disk_img = efi_capsule_data
|
||||
with u_boot_console.log.section('Test Case 1-a, before reboot'):
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
|
||||
'efidebug boot order 1',
|
||||
'env set -e OsIndications',
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'env save'])
|
||||
|
||||
# initialize contents
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
|
||||
'sf write 4000000 100000 10',
|
||||
'sf read 5000000 100000 10',
|
||||
'md.b 5000000 10'])
|
||||
assert 'Old' in ''.join(output)
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
|
||||
'sf write 4000000 150000 10',
|
||||
'sf read 5000000 150000 10',
|
||||
'md.b 5000000 10'])
|
||||
assert 'Old' in ''.join(output)
|
||||
|
||||
# place a capsule file
|
||||
output = u_boot_console.run_command_list([
|
||||
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
|
||||
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' in ''.join(output)
|
||||
|
||||
# reboot
|
||||
u_boot_console.restart_uboot()
|
||||
|
||||
capsule_early = u_boot_config.buildconfig.get(
|
||||
'config_efi_capsule_on_disk_early')
|
||||
with u_boot_console.log.section('Test Case 1-b, after reboot'):
|
||||
if not capsule_early:
|
||||
# make sure that dfu_alt_info exists even persistent variables
|
||||
# are not available.
|
||||
output = u_boot_console.run_command_list([
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' in ''.join(output)
|
||||
|
||||
# need to run uefi command to initiate capsule handling
|
||||
output = u_boot_console.run_command(
|
||||
'env print -e -all Capsule0000')
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' in ''.join(output)
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'sf read 4000000 100000 10',
|
||||
'md.b 4000000 10'])
|
||||
assert 'u-boot:Old' in ''.join(output)
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf read 4000000 150000 10',
|
||||
'md.b 4000000 10'])
|
||||
assert 'u-boot-env:Old' in ''.join(output)
|
||||
|
||||
def test_efi_capsule_fw2(
|
||||
self, u_boot_config, u_boot_console, efi_capsule_data):
|
||||
"""
|
||||
Test Case 2 - Update U-Boot and U-Boot environment on SPI Flash
|
||||
0x100000-0x150000: U-Boot binary (but dummy)
|
||||
0x150000-0x200000: U-Boot environment (but dummy)
|
||||
"""
|
||||
disk_img = efi_capsule_data
|
||||
with u_boot_console.log.section('Test Case 2-a, before reboot'):
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
|
||||
'efidebug boot order 1',
|
||||
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'env save'])
|
||||
|
||||
# initialize contents
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
|
||||
'sf write 4000000 100000 10',
|
||||
'sf read 5000000 100000 10',
|
||||
'md.b 5000000 10'])
|
||||
assert 'Old' in ''.join(output)
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
|
||||
'sf write 4000000 150000 10',
|
||||
'sf read 5000000 150000 10',
|
||||
'md.b 5000000 10'])
|
||||
assert 'Old' in ''.join(output)
|
||||
|
||||
# place a capsule file
|
||||
output = u_boot_console.run_command_list([
|
||||
'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
|
||||
'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' in ''.join(output)
|
||||
|
||||
# reboot
|
||||
u_boot_console.restart_uboot()
|
||||
|
||||
capsule_early = u_boot_config.buildconfig.get(
|
||||
'config_efi_capsule_on_disk_early')
|
||||
with u_boot_console.log.section('Test Case 2-b, after reboot'):
|
||||
if not capsule_early:
|
||||
# make sure that dfu_alt_info exists even persistent variables
|
||||
# are not available.
|
||||
output = u_boot_console.run_command_list([
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' in ''.join(output)
|
||||
|
||||
# need to run uefi command to initiate capsule handling
|
||||
output = u_boot_console.run_command(
|
||||
'env print -e -all Capsule0000')
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test01' not in ''.join(output)
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'sf read 4000000 100000 10',
|
||||
'md.b 4000000 10'])
|
||||
assert 'u-boot:New' in ''.join(output)
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf read 4000000 150000 10',
|
||||
'md.b 4000000 10'])
|
||||
assert 'u-boot-env:New' in ''.join(output)
|
||||
|
||||
def test_efi_capsule_fw3(
|
||||
self, u_boot_config, u_boot_console, efi_capsule_data):
|
||||
"""
|
||||
Test Case 3 - Update U-Boot on SPI Flash, raw image format
|
||||
0x100000-0x150000: U-Boot binary (but dummy)
|
||||
"""
|
||||
disk_img = efi_capsule_data
|
||||
with u_boot_console.log.section('Test Case 3-a, before reboot'):
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
|
||||
'efidebug boot order 1',
|
||||
'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'env save'])
|
||||
|
||||
# initialize content
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
|
||||
'sf write 4000000 100000 10',
|
||||
'sf read 5000000 100000 10',
|
||||
'md.b 5000000 10'])
|
||||
assert 'Old' in ''.join(output)
|
||||
|
||||
# place a capsule file
|
||||
output = u_boot_console.run_command_list([
|
||||
'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
|
||||
'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test02' in ''.join(output)
|
||||
|
||||
# reboot
|
||||
u_boot_console.restart_uboot()
|
||||
|
||||
capsule_early = u_boot_config.buildconfig.get(
|
||||
'config_efi_capsule_on_disk_early')
|
||||
with u_boot_console.log.section('Test Case 3-b, after reboot'):
|
||||
if not capsule_early:
|
||||
# make sure that dfu_alt_info exists even persistent variables
|
||||
# are not available.
|
||||
output = u_boot_console.run_command_list([
|
||||
'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test02' in ''.join(output)
|
||||
|
||||
# need to run uefi command to initiate capsule handling
|
||||
output = u_boot_console.run_command(
|
||||
'env print -e -all Capsule0000')
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % disk_img,
|
||||
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
|
||||
assert 'Test02' not in ''.join(output)
|
||||
|
||||
output = u_boot_console.run_command_list([
|
||||
'sf probe 0:0',
|
||||
'sf read 4000000 100000 10',
|
||||
'md.b 4000000 10'])
|
||||
assert 'u-boot:New' in ''.join(output)
|
||||
36
test/py/tests/test_efi_capsule/uboot_bin_env.its
Normal file
36
test/py/tests/test_efi_capsule/uboot_bin_env.its
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Automatic software update for U-Boot
|
||||
* Make sure the flashing addresses ('load' prop) is correct for your board!
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
description = "Automatic U-Boot environment update";
|
||||
#address-cells = <2>;
|
||||
|
||||
images {
|
||||
u-boot-bin@100000 {
|
||||
description = "U-Boot binary on SPI Flash";
|
||||
data = /incbin/("BINFILE1");
|
||||
compression = "none";
|
||||
type = "firmware";
|
||||
arch = "sandbox";
|
||||
load = <0>;
|
||||
hash-1 {
|
||||
algo = "sha1";
|
||||
};
|
||||
};
|
||||
u-boot-env@150000 {
|
||||
description = "U-Boot environment on SPI Flash";
|
||||
data = /incbin/("BINFILE2");
|
||||
compression = "none";
|
||||
type = "firmware";
|
||||
arch = "sandbox";
|
||||
load = <0>;
|
||||
hash-1 {
|
||||
algo = "sha1";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -218,6 +218,8 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
|
||||
hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
|
||||
HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
|
||||
|
||||
hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
|
||||
|
||||
# We build some files with extra pedantic flags to try to minimize things
|
||||
# that won't build on some weird host compiler -- though there are lots of
|
||||
# exceptions for files that aren't complaint.
|
||||
|
||||
237
tools/mkeficapsule.c
Normal file
237
tools/mkeficapsule.c
Normal file
@@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 Linaro Limited
|
||||
* Author: AKASHI Takahiro
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <linux/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef __u8 u8;
|
||||
typedef __u16 u16;
|
||||
typedef __u32 u32;
|
||||
typedef __u64 u64;
|
||||
typedef __s16 s16;
|
||||
typedef __s32 s32;
|
||||
|
||||
#define aligned_u64 __aligned_u64
|
||||
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#include <efi.h>
|
||||
#include <efi_api.h>
|
||||
|
||||
static const char *tool_name = "mkeficapsule";
|
||||
|
||||
efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
|
||||
efi_guid_t efi_guid_image_type_uboot_fit =
|
||||
EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
|
||||
efi_guid_t efi_guid_image_type_uboot_raw =
|
||||
EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
|
||||
|
||||
static struct option options[] = {
|
||||
{"fit", required_argument, NULL, 'f'},
|
||||
{"raw", required_argument, NULL, 'r'},
|
||||
{"index", required_argument, NULL, 'i'},
|
||||
{"instance", required_argument, NULL, 'I'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
||||
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",
|
||||
tool_name);
|
||||
}
|
||||
|
||||
static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
|
||||
unsigned long index, unsigned long instance)
|
||||
{
|
||||
struct efi_capsule_header header;
|
||||
struct efi_firmware_management_capsule_header capsule;
|
||||
struct efi_firmware_management_capsule_image_header image;
|
||||
FILE *f, *g;
|
||||
struct stat bin_stat;
|
||||
u8 *data;
|
||||
size_t size;
|
||||
u64 offset;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("For output: %s\n", path);
|
||||
printf("\tbin: %s\n\ttype: %pUl\n" bin, guid);
|
||||
printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
|
||||
#endif
|
||||
|
||||
g = fopen(bin, "r");
|
||||
if (!g) {
|
||||
printf("cannot open %s\n", bin);
|
||||
return -1;
|
||||
}
|
||||
if (stat(bin, &bin_stat) < 0) {
|
||||
printf("cannot determine the size of %s\n", bin);
|
||||
goto err_1;
|
||||
}
|
||||
data = malloc(bin_stat.st_size);
|
||||
if (!data) {
|
||||
printf("cannot allocate memory: %lx\n", bin_stat.st_size);
|
||||
goto err_1;
|
||||
}
|
||||
f = fopen(path, "w");
|
||||
if (!f) {
|
||||
printf("cannot open %s\n", path);
|
||||
goto err_2;
|
||||
}
|
||||
header.capsule_guid = efi_guid_fm_capsule;
|
||||
header.header_size = sizeof(header);
|
||||
/* TODO: The current implementation ignores flags */
|
||||
header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
|
||||
header.capsule_image_size = sizeof(header)
|
||||
+ sizeof(capsule) + sizeof(u64)
|
||||
+ sizeof(image)
|
||||
+ bin_stat.st_size;
|
||||
|
||||
size = fwrite(&header, 1, sizeof(header), f);
|
||||
if (size < sizeof(header)) {
|
||||
printf("write failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
|
||||
capsule.version = 0x00000001;
|
||||
capsule.embedded_driver_count = 0;
|
||||
capsule.payload_item_count = 1;
|
||||
size = fwrite(&capsule, 1, sizeof(capsule), f);
|
||||
if (size < (sizeof(capsule))) {
|
||||
printf("write failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
offset = sizeof(capsule) + sizeof(u64);
|
||||
size = fwrite(&offset, 1, sizeof(offset), f);
|
||||
if (size < sizeof(offset)) {
|
||||
printf("write failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
|
||||
image.version = 0x00000003;
|
||||
memcpy(&image.update_image_type_id, guid, sizeof(*guid));
|
||||
image.update_image_index = index;
|
||||
image.update_image_size = bin_stat.st_size;
|
||||
image.update_vendor_code_size = 0; /* none */
|
||||
image.update_hardware_instance = instance;
|
||||
image.image_capsule_support = 0;
|
||||
|
||||
size = fwrite(&image, 1, sizeof(image), f);
|
||||
if (size < sizeof(image)) {
|
||||
printf("write failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
size = fread(data, 1, bin_stat.st_size, g);
|
||||
if (size < bin_stat.st_size) {
|
||||
printf("read failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
size = fwrite(data, 1, bin_stat.st_size, f);
|
||||
if (size < bin_stat.st_size) {
|
||||
printf("write failed (%lx)\n", size);
|
||||
goto err_3;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
fclose(g);
|
||||
free(data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_3:
|
||||
fclose(f);
|
||||
err_2:
|
||||
free(data);
|
||||
err_1:
|
||||
fclose(g);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* $ mkeficapsule -f <firmware binary> <output file>
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *file;
|
||||
efi_guid_t *guid;
|
||||
unsigned long index, instance;
|
||||
int c, idx;
|
||||
|
||||
file = NULL;
|
||||
guid = NULL;
|
||||
index = 0;
|
||||
instance = 0;
|
||||
for (;;) {
|
||||
c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'f':
|
||||
if (file) {
|
||||
printf("Image already specified\n");
|
||||
return -1;
|
||||
}
|
||||
file = optarg;
|
||||
guid = &efi_guid_image_type_uboot_fit;
|
||||
break;
|
||||
case 'r':
|
||||
if (file) {
|
||||
printf("Image already specified\n");
|
||||
return -1;
|
||||
}
|
||||
file = optarg;
|
||||
guid = &efi_guid_image_type_uboot_raw;
|
||||
break;
|
||||
case 'i':
|
||||
index = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'I':
|
||||
instance = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* need a output file */
|
||||
if (argc != optind + 1) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* need a fit image file or raw image file */
|
||||
if (!file) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (create_fwbin(argv[optind], file, guid, index, instance)
|
||||
< 0) {
|
||||
printf("Creating firmware capsule failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user