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:
Tom Rini
2020-12-03 16:21:51 -05:00
25 changed files with 3282 additions and 97 deletions

View File

@@ -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"

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) */

View File

@@ -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,

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View 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 */

View 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 */

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
# Directories
CAPSULE_DATA_DIR = '/EFI/CapsuleTestData'
CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'

View 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)

View 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)

View 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";
};
};
};
};

View File

@@ -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
View 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;
}