diff options
Diffstat (limited to 'lib')
34 files changed, 3276 insertions, 439 deletions
| diff --git a/lib/Kconfig b/lib/Kconfig index 7673d2e4e04..a704568443c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -168,7 +168,7 @@ config REGEX  choice  	prompt "Pseudo-random library support type"  	depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID || \ -		   RNG_SANDBOX || UT_LIB && AES +		   RNG_SANDBOX || UT_LIB && AES || FAT_WRITE  	default LIB_RAND  	help  	  Select the library to provide pseudo-random number generator @@ -699,3 +699,11 @@ config LIB_ELF  	  This supports fir 32 bit and 64 bit versions.  endmenu + +config PHANDLE_CHECK_SEQ +	bool "Enable phandle check while getting sequence number" +	default n +	help +	  When there are multiple device tree nodes with same name, +          enable this config option to distinguish them using +	  phandles in fdtdec_get_alias_seq() function. diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index c3439a59883..b5f2cebbde1 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -602,7 +602,7 @@ static void acpi_device_write_i2c(struct acpi_ctx *ctx,  static int acpi_device_set_i2c(const struct udevice *dev, struct acpi_i2c *i2c,  			       const char *scope)  { -	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); +	struct dm_i2c_chip *chip = dev_get_parent_plat(dev);  	struct udevice *bus = dev_get_parent(dev);  	memset(i2c, '\0', sizeof(*i2c)); @@ -724,10 +724,10 @@ static void acpi_device_write_spi(struct acpi_ctx *ctx, const struct acpi_spi *s  static int acpi_device_set_spi(const struct udevice *dev, struct acpi_spi *spi,  			       const char *scope)  { -	struct dm_spi_slave_platdata *plat; +	struct dm_spi_slave_plat *plat;  	struct spi_slave *slave = dev_get_parent_priv(dev); -	plat = dev_get_parent_platdata(slave->dev); +	plat = dev_get_parent_plat(slave->dev);  	memset(spi, '\0', sizeof(*spi));  	spi->device_select = plat->cs;  	spi->device_select_polarity = SPI_POLARITY_LOW; @@ -784,16 +784,6 @@ static const char *acpi_name_from_id(enum uclass_id id)  	}  } -static int acpi_check_seq(const struct udevice *dev) -{ -	if (dev->req_seq == -1) { -		log_warning("Device '%s' has no seq\n", dev->name); -		return log_msg_ret("no seq", -ENXIO); -	} - -	return dev->req_seq; -} -  /* If you change this function, add test cases to dm_test_acpi_get_name() */  int acpi_device_infer_name(const struct udevice *dev, char *out_name)  { @@ -826,29 +816,18 @@ int acpi_device_infer_name(const struct udevice *dev, char *out_name)  		}  	}  	if (!name) { -		int num; -  		switch (id) {  		/* DSDT: acpi/lpss.asl */  		case UCLASS_SERIAL: -			num = acpi_check_seq(dev); -			if (num < 0) -				return num; -			sprintf(out_name, "URT%d", num); +			sprintf(out_name, "URT%d", dev_seq(dev));  			name = out_name;  			break;  		case UCLASS_I2C: -			num = acpi_check_seq(dev); -			if (num < 0) -				return num; -			sprintf(out_name, "I2C%d", num); +			sprintf(out_name, "I2C%d", dev_seq(dev));  			name = out_name;  			break;  		case UCLASS_SPI: -			num = acpi_check_seq(dev); -			if (num < 0) -				return num; -			sprintf(out_name, "SPI%d", num); +			sprintf(out_name, "SPI%d", dev_seq(dev));  			name = out_name;  			break;  		default: diff --git a/lib/crypto/pkcs7_verify.c b/lib/crypto/pkcs7_verify.c index 320ba49f79d..58683ef614c 100644 --- a/lib/crypto/pkcs7_verify.c +++ b/lib/crypto/pkcs7_verify.c @@ -50,8 +50,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,  	struct image_region regions[2];  	int ret = 0; -	/* The digest was calculated already. */ -	if (sig->digest) +	/* +	 * [RFC2315 9.3] +	 * If the authenticated attributes are present, +	 * the message-digest is calculated on the +	 * attributes present in the +	 * authenticatedAttributes field and not just +	 * the contents field +	 */ +	if (!sinfo->authattrs && sig->digest)  		return 0;  	if (!sinfo->sig->hash_algo) @@ -63,17 +70,25 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,  	else  		return -ENOPKG; -	sig->digest = calloc(1, sig->digest_size); -	if (!sig->digest) { -		pr_warn("Sig %u: Out of memory\n", sinfo->index); -		return -ENOMEM; -	} +	/* +	 * Calculate the hash only if the data is present. +	 * In case of authenticated variable and capsule, +	 * the hash has already been calculated on the +	 * efi_image_regions and populated +	 */ +	if (pkcs7->data) { +		sig->digest = calloc(1, sig->digest_size); +		if (!sig->digest) { +			pr_warn("Sig %u: Out of memory\n", sinfo->index); +			return -ENOMEM; +		} -	regions[0].data = pkcs7->data; -	regions[0].size = pkcs7->data_len; +		regions[0].data = pkcs7->data; +		regions[0].size = pkcs7->data_len; -	/* Digest the message [RFC2315 9.3] */ -	hash_calculate(sinfo->sig->hash_algo, regions, 1, sig->digest); +		/* Digest the message [RFC2315 9.3] */ +		hash_calculate(sinfo->sig->hash_algo, regions, 1, sig->digest); +	}  	/* However, if there are authenticated attributes, there must be a  	 * message digest attribute amongst them which corresponds to the diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index 7d650d512e3..b3393e47fae 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -67,7 +67,7 @@ void putc(const char ch)  		putc('\r');  	if (use_uart) { -		NS16550_t com_port = (NS16550_t)0x3f8; +		struct ns16550 *com_port = (struct ns16550 *)0x3f8;  		while ((inb((ulong)&com_port->lsr) & UART_LSR_THRE) == 0)  			; diff --git a/lib/efi_driver/efi_block_device.c b/lib/efi_driver/efi_block_device.c index 0e72a68bced..0937e3595a4 100644 --- a/lib/efi_driver/efi_block_device.c +++ b/lib/efi_driver/efi_block_device.c @@ -42,7 +42,7 @@   * handle	handle of the controller on which this driver is installed   * io		block io protocol proxied by this driver   */ -struct efi_blk_platdata { +struct efi_blk_plat {  	efi_handle_t		handle;  	struct efi_block_io	*io;  }; @@ -59,8 +59,8 @@ struct efi_blk_platdata {  static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,  			 void *buffer)  { -	struct efi_blk_platdata *platdata = dev_get_platdata(dev); -	struct efi_block_io *io = platdata->io; +	struct efi_blk_plat *plat = dev_get_plat(dev); +	struct efi_block_io *io = plat->io;  	efi_status_t ret;  	EFI_PRINT("%s: read '%s', from block " LBAFU ", " LBAFU " blocks\n", @@ -88,8 +88,8 @@ static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,  static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,  			  const void *buffer)  { -	struct efi_blk_platdata *platdata = dev_get_platdata(dev); -	struct efi_block_io *io = platdata->io; +	struct efi_blk_plat *plat = dev_get_plat(dev); +	struct efi_block_io *io = plat->io;  	efi_status_t ret;  	EFI_PRINT("%s: write '%s', from block " LBAFU ", " LBAFU " blocks\n", @@ -118,7 +118,7 @@ static int efi_bl_bind_partitions(efi_handle_t handle, struct udevice *dev)  	struct blk_desc *desc;  	const char *if_typename; -	desc = dev_get_uclass_platdata(dev); +	desc = dev_get_uclass_plat(dev);  	if_typename = blk_get_if_type_name(desc->if_type);  	return efi_disk_create_partitions(handle, desc, if_typename, @@ -140,7 +140,7 @@ static int efi_bl_bind(efi_handle_t handle, void *interface)  	struct efi_object *obj = efi_search_obj(handle);  	struct efi_block_io *io = interface;  	int disks; -	struct efi_blk_platdata *platdata; +	struct efi_blk_plat *plat;  	EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, io); @@ -169,9 +169,9 @@ static int efi_bl_bind(efi_handle_t handle, void *interface)  	/* Set the DM_FLAG_NAME_ALLOCED flag to avoid a memory leak */  	device_set_name_alloced(bdev); -	platdata = dev_get_platdata(bdev); -	platdata->handle = handle; -	platdata->io = interface; +	plat = dev_get_plat(bdev); +	plat->handle = handle; +	plat->io = interface;  	ret = device_probe(bdev);  	if (ret) @@ -196,7 +196,7 @@ U_BOOT_DRIVER(efi_blk) = {  	.name			= "efi_blk",  	.id			= UCLASS_BLK,  	.ops			= &efi_blk_ops, -	.platdata_auto_alloc_size = sizeof(struct efi_blk_platdata), +	.plat_auto	= sizeof(struct efi_blk_plat),  };  /* EFI driver operators */ diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c index 0cf74b0361d..382c2b477f4 100644 --- a/lib/efi_driver/efi_uclass.c +++ b/lib/efi_driver/efi_uclass.c @@ -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;  } diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index dd8b93bd3c5..fdf245dea30 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -108,6 +108,91 @@ 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_AUTHENTICATE +	bool "Update Capsule authentication" +	depends on EFI_CAPSULE_FIRMWARE +	depends on EFI_CAPSULE_ON_DISK +	depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT +	select SHA256 +	select RSA +	select RSA_VERIFY +	select RSA_VERIFY_WITH_PKEY +	select X509_CERTIFICATE_PARSER +	select PKCS7_MESSAGE_PARSER +	select PKCS7_VERIFY +	default n +	help +	  Select this option if you want to enable capsule +	  authentication + +config EFI_CAPSULE_FIRMWARE_FIT +	bool "FMP driver for FIT image" +	depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT +	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 @@ -179,7 +264,8 @@ config EFI_HAVE_RUNTIME_RESET  	# bool "Reset runtime service is available"  	bool  	default y -	depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || SYSRESET_X86 +	depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || \ +		   SANDBOX || SYSRESET_X86  config EFI_GRUB_ARM32_WORKAROUND  	bool "Workaround for GRUB on 32bit ARM" @@ -206,6 +292,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 diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index cd4b252a417..462d4d9ac45 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -23,12 +23,14 @@ 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  obj-y += efi_device_path_utilities.o  obj-y += efi_file.o -obj-$(CONFIG_EFI_LOADER_HII) += efi_hii.o efi_hii_config.o +obj-$(CONFIG_EFI_LOADER_HII) += efi_hii.o  obj-y += efi_image_loader.o  obj-y += efi_memory.o  obj-y += efi_root_node.o diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 61dc72a23da..d3be2f94c60 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -275,7 +275,7 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,  		memcpy(*load_options, lo.optional_data, size);  		ret = efi_set_load_options(*handle, size, *load_options);  	} else { -		load_options = NULL; +		*load_options = NULL;  	}  error: diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 246b59d3b35..b2cb0160c08 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -81,6 +81,9 @@ const efi_guid_t efi_guid_event_group_ready_to_boot =  /* event group ResetSystem() invoked (before ExitBootServices) */  const efi_guid_t efi_guid_event_group_reset_system =  			EFI_EVENT_GROUP_RESET_SYSTEM; +/* GUIDs of the Load File and Load File2 protocols */ +const efi_guid_t efi_guid_load_file_protocol = EFI_LOAD_FILE_PROTOCOL_GUID; +const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID;  static efi_status_t EFIAPI efi_disconnect_controller(  					efi_handle_t controller_handle, @@ -244,8 +247,8 @@ static void efi_queue_event(struct efi_event *event)  		}  		if (event)  			list_add_tail(&event->queue_link, &efi_event_queue); +		efi_process_event_queue();  	} -	efi_process_event_queue();  }  /** @@ -271,8 +274,8 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl)   * efi_signal_event() - signal an EFI event   * @event:     event to signal   * - * This function signals an event. If the event belongs to an event group all - * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL + * This function signals an event. If the event belongs to an event group, all + * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL,   * their notification function is queued.   *   * For the SignalEvent service see efi_signal_event_ext. @@ -1770,30 +1773,108 @@ failure:  }  /** - * efi_load_image_from_path() - load an image using a file path + * efi_locate_device_path() - Get the device path and handle of an device + *                            implementing a protocol + * @protocol:    GUID of the protocol + * @device_path: device path + * @device:      handle of the device + * + * This function implements the LocateDevicePath service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +static efi_status_t EFIAPI efi_locate_device_path( +			const efi_guid_t *protocol, +			struct efi_device_path **device_path, +			efi_handle_t *device) +{ +	struct efi_device_path *dp; +	size_t i; +	struct efi_handler *handler; +	efi_handle_t *handles; +	size_t len, len_dp; +	size_t len_best = 0; +	efi_uintn_t no_handles; +	u8 *remainder; +	efi_status_t ret; + +	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device); + +	if (!protocol || !device_path || !*device_path) { +		ret = EFI_INVALID_PARAMETER; +		goto out; +	} + +	/* Find end of device path */ +	len = efi_dp_instance_size(*device_path); + +	/* Get all handles implementing the protocol */ +	ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL, +						&no_handles, &handles)); +	if (ret != EFI_SUCCESS) +		goto out; + +	for (i = 0; i < no_handles; ++i) { +		/* Find the device path protocol */ +		ret = efi_search_protocol(handles[i], &efi_guid_device_path, +					  &handler); +		if (ret != EFI_SUCCESS) +			continue; +		dp = (struct efi_device_path *)handler->protocol_interface; +		len_dp = efi_dp_instance_size(dp); +		/* +		 * This handle can only be a better fit +		 * if its device path length is longer than the best fit and +		 * if its device path length is shorter of equal the searched +		 * device path. +		 */ +		if (len_dp <= len_best || len_dp > len) +			continue; +		/* Check if dp is a subpath of device_path */ +		if (memcmp(*device_path, dp, len_dp)) +			continue; +		if (!device) { +			ret = EFI_INVALID_PARAMETER; +			goto out; +		} +		*device = handles[i]; +		len_best = len_dp; +	} +	if (len_best) { +		remainder = (u8 *)*device_path + len_best; +		*device_path = (struct efi_device_path *)remainder; +		ret = EFI_SUCCESS; +	} else { +		ret = EFI_NOT_FOUND; +	} +out: +	return EFI_EXIT(ret); +} + +/** + * efi_load_image_from_file() - load an image from file system   *   * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the   * callers obligation to update the memory type as needed.   * - * @file_path:	the path of the image to load - * @buffer:	buffer containing the loaded image - * @size:	size of the loaded image - * Return:	status code + * @file_path:		the path of the image to load + * @buffer:		buffer containing the loaded image + * @size:		size of the loaded image + * Return:		status code   */  static -efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, +efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,  				      void **buffer, efi_uintn_t *size)  {  	struct efi_file_info *info = NULL;  	struct efi_file_handle *f; -	static efi_status_t ret; +	efi_status_t ret;  	u64 addr;  	efi_uintn_t bs; -	/* In case of failure nothing is returned */ -	*buffer = NULL; -	*size = 0; -  	/* Open file */  	f = efi_file_from_path(file_path);  	if (!f) @@ -1842,6 +1923,86 @@ error:  }  /** + * efi_load_image_from_path() - load an image using a file path + * + * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the + * callers obligation to update the memory type as needed. + * + * @boot_policy:	true for request originating from the boot manager + * @file_path:		the path of the image to load + * @buffer:		buffer containing the loaded image + * @size:		size of the loaded image + * Return:		status code + */ +static +efi_status_t efi_load_image_from_path(bool boot_policy, +				      struct efi_device_path *file_path, +				      void **buffer, efi_uintn_t *size) +{ +	efi_handle_t device; +	efi_status_t ret; +	struct efi_device_path *dp; +	struct efi_load_file_protocol *load_file_protocol = NULL; +	efi_uintn_t buffer_size; +	uint64_t addr, pages; +	const efi_guid_t *guid; + +	/* In case of failure nothing is returned */ +	*buffer = NULL; +	*size = 0; + +	dp = file_path; +	ret = EFI_CALL(efi_locate_device_path( +		       &efi_simple_file_system_protocol_guid, &dp, &device)); +	if (ret == EFI_SUCCESS) +		return efi_load_image_from_file(file_path, buffer, size); + +	ret = EFI_CALL(efi_locate_device_path( +		       &efi_guid_load_file_protocol, &dp, &device)); +	if (ret == EFI_SUCCESS) { +		guid = &efi_guid_load_file_protocol; +	} else if (!boot_policy) { +		guid = &efi_guid_load_file2_protocol; +		ret = EFI_CALL(efi_locate_device_path(guid, &dp, &device)); +	} +	if (ret != EFI_SUCCESS) +		return EFI_NOT_FOUND; +	ret = EFI_CALL(efi_handle_protocol(device, guid, +					   (void **)&load_file_protocol)); +	if (ret != EFI_SUCCESS) +		return EFI_NOT_FOUND; +	buffer_size = 0; +	ret = load_file_protocol->load_file(load_file_protocol, dp, +					    boot_policy, &buffer_size, +					    NULL); +	if (ret != EFI_BUFFER_TOO_SMALL) +		goto out; +	pages = efi_size_in_pages(buffer_size); +	ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_BOOT_SERVICES_DATA, +				 pages, &addr); +	if (ret != EFI_SUCCESS) { +		ret = EFI_OUT_OF_RESOURCES; +		goto out; +	} +	ret = EFI_CALL(load_file_protocol->load_file( +					load_file_protocol, dp, boot_policy, +					&buffer_size, (void *)(uintptr_t)addr)); +	if (ret != EFI_SUCCESS) +		efi_free_pages(addr, pages); +out: +	if (load_file_protocol) +		EFI_CALL(efi_close_protocol(device, +					    &efi_guid_load_file2_protocol, +					    efi_root, NULL)); +	if (ret == EFI_SUCCESS) { +		*buffer = (void *)(uintptr_t)addr; +		*size = buffer_size; +	} + +	return ret; +} + +/**   * efi_load_image() - load an EFI image into memory   * @boot_policy:   true for request originating from the boot manager   * @parent_image:  the caller's image handle @@ -1883,8 +2044,8 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,  	}  	if (!source_buffer) { -		ret = efi_load_image_from_path(file_path, &dest_buffer, -					       &source_size); +		ret = efi_load_image_from_path(boot_policy, file_path, +					       &dest_buffer, &source_size);  		if (ret != EFI_SUCCESS)  			goto error;  	} else { @@ -2000,7 +2161,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,  	}  	if (!efi_st_keep_devices) { -		if IS_ENABLED(CONFIG_USB_DEVICE) +		if (IS_ENABLED(CONFIG_USB_DEVICE))  			udc_disconnect();  		board_quiesce_devices();  		dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); @@ -2404,88 +2565,6 @@ found:  }  /** - * efi_locate_device_path() - Get the device path and handle of an device - *                            implementing a protocol - * @protocol:    GUID of the protocol - * @device_path: device path - * @device:      handle of the device - * - * This function implements the LocateDevicePath service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -static efi_status_t EFIAPI efi_locate_device_path( -			const efi_guid_t *protocol, -			struct efi_device_path **device_path, -			efi_handle_t *device) -{ -	struct efi_device_path *dp; -	size_t i; -	struct efi_handler *handler; -	efi_handle_t *handles; -	size_t len, len_dp; -	size_t len_best = 0; -	efi_uintn_t no_handles; -	u8 *remainder; -	efi_status_t ret; - -	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device); - -	if (!protocol || !device_path || !*device_path) { -		ret = EFI_INVALID_PARAMETER; -		goto out; -	} - -	/* Find end of device path */ -	len = efi_dp_instance_size(*device_path); - -	/* Get all handles implementing the protocol */ -	ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL, -						&no_handles, &handles)); -	if (ret != EFI_SUCCESS) -		goto out; - -	for (i = 0; i < no_handles; ++i) { -		/* Find the device path protocol */ -		ret = efi_search_protocol(handles[i], &efi_guid_device_path, -					  &handler); -		if (ret != EFI_SUCCESS) -			continue; -		dp = (struct efi_device_path *)handler->protocol_interface; -		len_dp = efi_dp_instance_size(dp); -		/* -		 * This handle can only be a better fit -		 * if its device path length is longer than the best fit and -		 * if its device path length is shorter of equal the searched -		 * device path. -		 */ -		if (len_dp <= len_best || len_dp > len) -			continue; -		/* Check if dp is a subpath of device_path */ -		if (memcmp(*device_path, dp, len_dp)) -			continue; -		if (!device) { -			ret = EFI_INVALID_PARAMETER; -			goto out; -		} -		*device = handles[i]; -		len_best = len_dp; -	} -	if (len_best) { -		remainder = (u8 *)*device_path + len_best; -		*device_path = (struct efi_device_path *)remainder; -		ret = EFI_SUCCESS; -	} else { -		ret = EFI_NOT_FOUND; -	} -out: -	return EFI_EXIT(ret); -} - -/**   * efi_install_multiple_protocol_interfaces() - Install multiple protocol   *                                              interfaces   * @handle: handle on which the protocol interfaces shall be installed @@ -2700,7 +2779,7 @@ static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)   *   * Return: status code   */ -static efi_status_t efi_protocol_open( +efi_status_t efi_protocol_open(  			struct efi_handler *handler,  			void **protocol_interface, void *agent_handle,  			void *controller_handle, uint32_t attributes) @@ -2899,6 +2978,8 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,  	efi_status_t ret;  	void *info;  	efi_handle_t parent_image = current_image; +	efi_status_t exit_status; +	struct jmp_buf_data exit_jmp;  	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); @@ -2920,9 +3001,11 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,  	image_obj->exit_data_size = exit_data_size;  	image_obj->exit_data = exit_data; +	image_obj->exit_status = &exit_status; +	image_obj->exit_jmp = &exit_jmp;  	/* call the image! */ -	if (setjmp(&image_obj->exit_jmp)) { +	if (setjmp(&exit_jmp)) {  		/*  		 * We called the entry point of the child image with EFI_CALL  		 * in the lines below. The child image called the Exit() boot @@ -2944,10 +3027,10 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,  		 */  		assert(__efi_entry_check());  		EFI_PRINT("%lu returned by started image\n", -			  (unsigned long)((uintptr_t)image_obj->exit_status & +			  (unsigned long)((uintptr_t)exit_status &  			  ~EFI_ERROR_MASK));  		current_image = parent_image; -		return EFI_EXIT(image_obj->exit_status); +		return EFI_EXIT(exit_status);  	}  	current_image = image_handle; @@ -3130,6 +3213,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,  	struct efi_loaded_image *loaded_image_protocol;  	struct efi_loaded_image_obj *image_obj =  		(struct efi_loaded_image_obj *)image_handle; +	struct jmp_buf_data *exit_jmp;  	EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,  		  exit_data_size, exit_data); @@ -3171,6 +3255,9 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,  		if (ret != EFI_SUCCESS)  			EFI_PRINT("%s: out of memory\n", __func__);  	} +	/* efi_delete_image() frees image_obj. Copy before the call. */ +	exit_jmp = image_obj->exit_jmp; +	*image_obj->exit_status = exit_status;  	if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||  	    exit_status != EFI_SUCCESS)  		efi_delete_image(image_obj, loaded_image_protocol); @@ -3184,8 +3271,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,  	 */  	efi_restore_gd(); -	image_obj->exit_status = exit_status; -	longjmp(&image_obj->exit_jmp, 1); +	longjmp(exit_jmp, 1);  	panic("EFI application exited");  out: diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c new file mode 100644 index 00000000000..dad1b0fcf7c --- /dev/null +++ b/lib/efi_loader/efi_capsule.c @@ -0,0 +1,1032 @@ +// 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> + +#include <crypto/pkcs7.h> +#include <crypto/pkcs7_parser.h> +#include <linux/err.h> + +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; +static const efi_guid_t efi_guid_firmware_management_capsule_id = +		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; +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, sizeof(variable_name16), +				"Capsule", index); +	result.variable_total_size = sizeof(result); +	result.capsule_guid = capsule->capsule_guid; +	ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL)); +	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; +} + +#if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE) + +const efi_guid_t efi_guid_capsule_root_cert_guid = +	EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; + +__weak int efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len) +{ +	/* The platform is supposed to provide +	 * a method for getting the public key +	 * stored in the form of efi signature +	 * list +	 */ +	return 0; +} + +efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size, +				      void **image, efi_uintn_t *image_size) +{ +	u8 *buf; +	int ret; +	void *fdt_pkey, *pkey; +	efi_uintn_t pkey_len; +	uint64_t monotonic_count; +	struct efi_signature_store *truststore; +	struct pkcs7_message *capsule_sig; +	struct efi_image_regions *regs; +	struct efi_firmware_image_authentication *auth_hdr; +	efi_status_t status; + +	status = EFI_SECURITY_VIOLATION; +	capsule_sig = NULL; +	truststore = NULL; +	regs = NULL; + +	/* Sanity checks */ +	if (capsule == NULL || capsule_size == 0) +		goto out; + +	auth_hdr = (struct efi_firmware_image_authentication *)capsule; +	if (capsule_size < sizeof(*auth_hdr)) +		goto out; + +	if (auth_hdr->auth_info.hdr.dwLength <= +	    offsetof(struct win_certificate_uefi_guid, cert_data)) +		goto out; + +	if (guidcmp(&auth_hdr->auth_info.cert_type, &efi_guid_cert_type_pkcs7)) +		goto out; + +	*image = (uint8_t *)capsule + sizeof(auth_hdr->monotonic_count) + +		auth_hdr->auth_info.hdr.dwLength; +	*image_size = capsule_size - auth_hdr->auth_info.hdr.dwLength - +		sizeof(auth_hdr->monotonic_count); +	memcpy(&monotonic_count, &auth_hdr->monotonic_count, +	       sizeof(monotonic_count)); + +	/* data to be digested */ +	regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 2, 1); +	if (!regs) +		goto out; + +	regs->max = 2; +	efi_image_region_add(regs, (uint8_t *)*image, +			     (uint8_t *)*image + *image_size, 1); + +	efi_image_region_add(regs, (uint8_t *)&monotonic_count, +			     (uint8_t *)&monotonic_count + sizeof(monotonic_count), +			     1); + +	capsule_sig = efi_parse_pkcs7_header(auth_hdr->auth_info.cert_data, +					     auth_hdr->auth_info.hdr.dwLength +					     - sizeof(auth_hdr->auth_info), +					     &buf); +	if (IS_ERR(capsule_sig)) { +		debug("Parsing variable's pkcs7 header failed\n"); +		capsule_sig = NULL; +		goto out; +	} + +	ret = efi_get_public_key_data(&fdt_pkey, &pkey_len); +	if (ret < 0) +		goto out; + +	pkey = malloc(pkey_len); +	if (!pkey) +		goto out; + +	memcpy(pkey, fdt_pkey, pkey_len); +	truststore = efi_build_signature_store(pkey, pkey_len); +	if (!truststore) +		goto out; + +	/* verify signature */ +	if (efi_signature_verify(regs, capsule_sig, truststore, NULL)) { +		debug("Verified\n"); +	} else { +		debug("Verifying variable's signature failed\n"); +		goto out; +	} + +	status = EFI_SUCCESS; + +out: +	efi_sigstore_free(truststore); +	pkcs7_free_message(capsule_sig); +	free(regs); + +	return status; +} +#else +efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size, +				      void **image, efi_uintn_t *image_size) +{ +	return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */ + + +/** + * efi_capsule_update_firmware - update firmware from capsule + * @capsule_data:	Capsule + * + * 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, sizeof(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 */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 011accab789..705109596eb 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -14,6 +14,7 @@  #include <env.h>  #include <stdio_dev.h>  #include <video_console.h> +#include <linux/delay.h>  #define EFI_COUT_MODE_2 2  #define EFI_MAX_COUT_MODE 3 @@ -689,6 +690,17 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key)  	switch (ch) {  	case 0x1b:  		/* +		 * If a second key is received within 10 ms, assume that we are +		 * dealing with an escape sequence. Otherwise consider this the +		 * escape key being hit. 10 ms is long enough to work fine at +		 * 1200 baud and above. +		 */ +		udelay(10000); +		if (!tstc()) { +			pressed_key.scan_code = 23; +			break; +		} +		/*  		 * Xterm Control Sequences  		 * https://www.xfree86.org/4.8.0/ctlseqs.html  		 */ diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 8a5c13c4241..c9315dd4585 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -531,7 +531,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  	case UCLASS_ETH: {  		struct efi_device_path_mac_addr *dp =  			dp_fill(buf, dev->parent); -		struct eth_pdata *pdata = dev->platdata; +		struct eth_pdata *pdata = dev_get_plat(dev);  		dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;  		dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; @@ -551,7 +551,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  		case UCLASS_ROOT: {  			/* stop traversing parents at this point: */  			struct efi_device_path_vendor *dp; -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			dp_fill(buf, dev->parent);  			dp = buf; @@ -568,7 +568,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  #ifdef CONFIG_VIRTIO_BLK  		case UCLASS_VIRTIO: {  			struct efi_device_path_vendor *dp; -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			dp_fill(buf, dev->parent);  			dp = buf; @@ -586,7 +586,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  		case UCLASS_IDE: {  			struct efi_device_path_atapi *dp =  			dp_fill(buf, dev->parent); -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;  			dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; @@ -603,7 +603,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  		case UCLASS_SCSI: {  			struct efi_device_path_scsi *dp =  				dp_fill(buf, dev->parent); -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;  			dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; @@ -617,14 +617,14 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  		case UCLASS_MMC: {  			struct efi_device_path_sd_mmc_path *sddp =  				dp_fill(buf, dev->parent); -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			sddp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;  			sddp->dp.sub_type = is_sd(desc) ?  				DEVICE_PATH_SUB_TYPE_MSG_SD :  				DEVICE_PATH_SUB_TYPE_MSG_MMC;  			sddp->dp.length   = sizeof(*sddp); -			sddp->slot_number = dev->seq; +			sddp->slot_number = dev_seq(dev);  			return &sddp[1];  			}  #endif @@ -632,7 +632,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  		case UCLASS_AHCI: {  			struct efi_device_path_sata *dp =  				dp_fill(buf, dev->parent); -			struct blk_desc *desc = dev_get_uclass_platdata(dev); +			struct blk_desc *desc = dev_get_uclass_plat(dev);  			dp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;  			dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SATA; @@ -677,7 +677,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)  			DEVICE_PATH_SUB_TYPE_MSG_SD :  			DEVICE_PATH_SUB_TYPE_MSG_MMC;  		sddp->dp.length   = sizeof(*sddp); -		sddp->slot_number = dev->seq; +		sddp->slot_number = dev_seq(dev);  		return &sddp[1];  	} diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 7bd1ccec450..26b953461bc 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -376,6 +376,23 @@ static efi_status_t efi_disk_add_dev(  	/* Fill in object data */  	if (part) {  		struct efi_device_path *node = efi_dp_part_node(desc, part); +		struct efi_handler *handler; +		void *protocol_interface; + +		/* Parent must expose EFI_BLOCK_IO_PROTOCOL */ +		ret = efi_search_protocol(parent, &efi_block_io_guid, &handler); +		if (ret != EFI_SUCCESS) +			goto error; + +		/* +		 * Link the partition (child controller) to the block device +		 * (controller). +		 */ +		ret = efi_protocol_open(handler, &protocol_interface, NULL, +					&diskobj->header, +					EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); +		if (ret != EFI_SUCCESS) +				goto error;  		diskobj->dp = efi_dp_append_node(dp_parent, node);  		efi_free_pool(node); @@ -453,6 +470,9 @@ static efi_status_t efi_disk_add_dev(  		}  	}  	return EFI_SUCCESS; +error: +	efi_delete_handle(&diskobj->header); +	return ret;  }  /** @@ -527,7 +547,7 @@ efi_status_t efi_disk_register(void)  	for (uclass_first_device_check(UCLASS_BLK, &dev); dev;  	     uclass_next_device_check(&dev)) { -		struct blk_desc *desc = dev_get_uclass_platdata(dev); +		struct blk_desc *desc = dev_get_uclass_plat(dev);  		const char *if_typename = blk_get_if_type_name(desc->if_type);  		/* Add block device for the full device */ diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c new file mode 100644 index 00000000000..5e401bbca2b --- /dev/null +++ b/lib/efi_loader/efi_firmware.c @@ -0,0 +1,478 @@ +// 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 <signatures.h> + +#include <linux/list.h> + +#define FMP_PAYLOAD_HDR_SIGNATURE	SIGNATURE_32('M', 'S', 'S', '1') + +/** + * struct fmp_payload_header - EDK2 header for the FMP payload + * + * This structure describes the header which is preprended to the + * FMP payload by the edk2 capsule generation scripts. + * + * @signature:			Header signature used to identify the header + * @header_size:		Size of the structure + * @fw_version:			Firmware versions used + * @lowest_supported_version:	Lowest supported version + */ +struct fmp_payload_header { +	u32 signature; +	u32 header_size; +	u32 fw_version; +	u32 lowest_supported_version; +}; + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_get_image_unsupported( +	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_ATTRIBUTE_AUTHENTICATION_REQUIRED; +		image_info[i].attributes_setting = +				IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + +		/* Check if the capsule authentication is enabled */ +		if (env_get("capsule_authentication_enabled")) +			image_info[0].attributes_setting |= +				IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED; + +		image_info[i].lowest_supported_image_version = 0; +		image_info[i].last_attempt_version = 0; +		image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; +		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) +{ +	u32 fmp_hdr_signature; +	struct fmp_payload_header *header; +	void *capsule_payload; +	efi_status_t status; +	efi_uintn_t capsule_payload_size; + +	EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image, +		  image_size, vendor_code, progress, abort_reason); + +	if (!image) +		return EFI_EXIT(EFI_INVALID_PARAMETER); + +	/* Authenticate the capsule if authentication enabled */ +	if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) && +	    env_get("capsule_authentication_enabled")) { +		capsule_payload = NULL; +		capsule_payload_size = 0; +		status = efi_capsule_authenticate(image, image_size, +						  &capsule_payload, +						  &capsule_payload_size); + +		if (status == EFI_SECURITY_VIOLATION) { +			printf("Capsule authentication check failed. Aborting update\n"); +			return EFI_EXIT(status); +		} else if (status != EFI_SUCCESS) { +			return EFI_EXIT(status); +		} + +		debug("Capsule authentication successfull\n"); +		image = capsule_payload; +		image_size = capsule_payload_size; +	} else { +		debug("Capsule authentication disabled. "); +		debug("Updating capsule without authenticating.\n"); +	} + +	fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; +	header = (void *)image; + +	if (!memcmp(&header->signature, &fmp_hdr_signature, +		    sizeof(fmp_hdr_signature))) { +		/* +		 * When building the capsule with the scripts in +		 * edk2, a FMP header is inserted above the capsule +		 * payload. Compensate for this header to get the +		 * actual payload that is to be updated. +		 */ +		image += header->header_size; +		image_size -= header->header_size; + +	} + +	if (dfu_write_by_alt(image_index - 1, (void *)image, image_size, +			     NULL, NULL)) +		return EFI_EXIT(EFI_DEVICE_ERROR); + +	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 */ diff --git a/lib/efi_loader/efi_hii_config.c b/lib/efi_loader/efi_hii_config.c index 26ea4b9bc0a..237e8acf840 100644 --- a/lib/efi_loader/efi_hii_config.c +++ b/lib/efi_loader/efi_hii_config.c @@ -1,9 +1,13 @@  // SPDX-License-Identifier:     GPL-2.0+  /* - *  EFI Human Interface Infrastructure ... Configuration + * EFI Human Interface Infrastructure ... Configuration   * - *  Copyright (c) 2017 Leif Lindholm - *  Copyright (c) 2018 AKASHI Takahiro, Linaro Limited + * Copyright (c) 2017 Leif Lindholm + * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited + * + * As this is still a non-working stub and the protocol is neither required + * by the EFI shell nor by the UEFI SCT this module has been removed from + * the Makefile.   */  #include <common.h> diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c index d517d686c33..b9ee8839054 100644 --- a/lib/efi_loader/efi_load_initrd.c +++ b/lib/efi_loader/efi_load_initrd.c @@ -4,16 +4,11 @@   */  #include <common.h> -#include <env.h> -#include <malloc.h> -#include <mapmem.h> -#include <dm.h> -#include <fs.h>  #include <efi_loader.h>  #include <efi_load_initrd.h> - -static const efi_guid_t efi_guid_load_file2_protocol = -		EFI_LOAD_FILE2_PROTOCOL_GUID; +#include <fs.h> +#include <malloc.h> +#include <mapmem.h>  static efi_status_t EFIAPI  efi_load_file2_initrd(struct efi_load_file_protocol *this, diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c index f68b0fdc610..b17db312f78 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -77,9 +77,6 @@ efi_status_t efi_root_node_register(void)  			 /* HII database protocol */  			 &efi_guid_hii_database_protocol,  			 (void *)&efi_hii_database, -			 /* HII configuration routing protocol */ -			 &efi_guid_hii_config_routing_protocol, -			 (void *)&efi_hii_config_routing,  #endif  			 NULL));  	efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE; diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 1fa1595e402..93c9478b225 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -449,6 +449,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)  }  /** + * 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   *   * @p:		pointer to check @@ -471,6 +515,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 +930,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 +947,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,  }; diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index e206b60bb82..5800cbf6d46 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -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) @@ -118,13 +118,67 @@ 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   *   * Return:	status code   */  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) @@ -209,11 +257,6 @@ efi_status_t efi_init_obj_list(void)  	if (ret != EFI_SUCCESS)  		goto out;  #endif -#ifdef CONFIG_EFI_LOAD_FILE2_INITRD -	ret = efi_initrd_register(); -	if (ret != EFI_SUCCESS) -		goto out; -#endif  #ifdef CONFIG_NET  	ret = efi_net_register();  	if (ret != EFI_SUCCESS) @@ -233,11 +276,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; diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 79dee27421b..c7ec2754147 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -26,7 +26,92 @@ const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;  const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;  const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; -#ifdef CONFIG_EFI_SECURE_BOOT +#if defined(CONFIG_EFI_SECURE_BOOT) || defined(CONFIG_EFI_CAPSULE_AUTHENTICATE) +static u8 pkcs7_hdr[] = { +	/* SEQUENCE */ +	0x30, 0x82, 0x05, 0xc7, +	/* OID: pkcs7-signedData */ +	0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, +	/* Context Structured? */ +	0xa0, 0x82, 0x05, 0xb8, +}; + +/** + * efi_parse_pkcs7_header - parse a signature in payload + * @buf:	Pointer to payload's value + * @buflen:	Length of @buf + * @tmpbuf:	Pointer to temporary buffer + * + * Parse a signature embedded in payload's value and instantiate + * a pkcs7_message structure. Since pkcs7_parse_message() accepts only + * pkcs7's signedData, some header needed be prepended for correctly + * parsing authentication data + * A temporary buffer will be allocated if needed, and it should be + * kept valid during the authentication because some data in the buffer + * will be referenced by efi_signature_verify(). + * + * Return:	Pointer to pkcs7_message structure on success, NULL on error + */ +struct pkcs7_message *efi_parse_pkcs7_header(const void *buf, +					     size_t buflen, +					     u8 **tmpbuf) +{ +	u8 *ebuf; +	size_t ebuflen, len; +	struct pkcs7_message *msg; + +	/* +	 * This is the best assumption to check if the binary is +	 * already in a form of pkcs7's signedData. +	 */ +	if (buflen > sizeof(pkcs7_hdr) && +	    !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { +		msg = pkcs7_parse_message(buf, buflen); +		if (IS_ERR(msg)) +			return NULL; +		return msg; +	} + +	/* +	 * Otherwise, we should add a dummy prefix sequence for pkcs7 +	 * message parser to be able to process. +	 * NOTE: EDK2 also uses similar hack in WrapPkcs7Data() +	 * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c +	 * TODO: +	 * The header should be composed in a more refined manner. +	 */ +	EFI_PRINT("Makeshift prefix added to authentication data\n"); +	ebuflen = sizeof(pkcs7_hdr) + buflen; +	if (ebuflen <= 0x7f) { +		EFI_PRINT("Data is too short\n"); +		return NULL; +	} + +	ebuf = malloc(ebuflen); +	if (!ebuf) { +		EFI_PRINT("Out of memory\n"); +		return NULL; +	} + +	memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr)); +	memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen); +	len = ebuflen - 4; +	ebuf[2] = (len >> 8) & 0xff; +	ebuf[3] = len & 0xff; +	len = ebuflen - 0x13; +	ebuf[0x11] = (len >> 8) & 0xff; +	ebuf[0x12] = len & 0xff; + +	msg = pkcs7_parse_message(ebuf, ebuflen); + +	if (IS_ERR(msg)) { +		free(ebuf); +		return NULL; +	} + +	*tmpbuf = ebuf; +	return msg; +}  /**   * efi_hash_regions - calculate a hash value @@ -652,6 +737,63 @@ err:  }  /** + * efi_sigstore_parse_sigdb - parse the signature list and populate + * the signature store + * + * @sig_list:	Pointer to the signature list + * @size:	Size of the signature list + * + * Parse the efi signature list and instantiate a signature store + * structure. + * + * Return:	Pointer to signature store on success, NULL on error + */ +struct efi_signature_store *efi_build_signature_store(void *sig_list, +						      efi_uintn_t size) +{ +	struct efi_signature_list *esl; +	struct efi_signature_store *sigstore = NULL, *siglist; + +	esl = sig_list; +	while (size > 0) { +		/* List must exist if there is remaining data. */ +		if (size < sizeof(*esl)) { +			EFI_PRINT("Signature list in wrong format\n"); +			goto err; +		} + +		if (size < esl->signature_list_size) { +			EFI_PRINT("Signature list in wrong format\n"); +			goto err; +		} + +		/* Parse a single siglist. */ +		siglist = efi_sigstore_parse_siglist(esl); +		if (!siglist) { +			EFI_PRINT("Parsing of signature list of failed\n"); +			goto err; +		} + +		/* Append siglist */ +		siglist->next = sigstore; +		sigstore = siglist; + +		/* Next */ +		size -= esl->signature_list_size; +		esl = (void *)esl + esl->signature_list_size; +	} +	free(sig_list); + +	return sigstore; + +err: +	efi_sigstore_free(sigstore); +	free(sig_list); + +	return NULL; +} + +/**   * efi_sigstore_parse_sigdb - parse a signature database variable   * @name:	Variable's name   * @@ -662,8 +804,7 @@ err:   */  struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)  { -	struct efi_signature_store *sigstore = NULL, *siglist; -	struct efi_signature_list *esl; +	struct efi_signature_store *sigstore = NULL;  	const efi_guid_t *vendor;  	void *db;  	efi_uintn_t db_size; @@ -699,47 +840,10 @@ struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)  	ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));  	if (ret != EFI_SUCCESS) {  		EFI_PRINT("Getting variable, %ls, failed\n", name); -		goto err; -	} - -	/* Parse siglist list */ -	esl = db; -	while (db_size > 0) { -		/* List must exist if there is remaining data. */ -		if (db_size < sizeof(*esl)) { -			EFI_PRINT("variable, %ls, in wrong format\n", name); -			goto err; -		} - -		if (db_size < esl->signature_list_size) { -			EFI_PRINT("variable, %ls, in wrong format\n", name); -			goto err; -		} - -		/* Parse a single siglist. */ -		siglist = efi_sigstore_parse_siglist(esl); -		if (!siglist) { -			EFI_PRINT("Parsing signature list of %ls failed\n", -				  name); -			goto err; -		} - -		/* Append siglist */ -		siglist->next = sigstore; -		sigstore = siglist; - -		/* Next */ -		db_size -= esl->signature_list_size; -		esl = (void *)esl + esl->signature_list_size; +		free(db); +		return NULL;  	} -	free(db); -	return sigstore; - -err: -	efi_sigstore_free(sigstore); -	free(db); - -	return NULL; +	return efi_build_signature_store(db, db_size);  } -#endif /* CONFIG_EFI_SECURE_BOOT */ +#endif /* CONFIG_EFI_SECURE_BOOT || CONFIG_EFI_CAPSULE_AUTHENTICATE */ diff --git a/lib/efi_loader/efi_string.c b/lib/efi_loader/efi_string.c index 3de721f06c7..96272422886 100644 --- a/lib/efi_loader/efi_string.c +++ b/lib/efi_loader/efi_string.c @@ -23,13 +23,19 @@   * Return: A pointer to the next position after the created string   *	   in @buffer, or NULL otherwise   */ -u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index) +u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name, +			     unsigned int index)  {  	u16 *p = buffer;  	char index_buf[5]; +	size_t size; +	size = (utf8_utf16_strlen(name) * sizeof(u16) + +		sizeof(index_buf) * sizeof(u16)); +	if (buffer_size < size) +		return NULL;  	utf8_utf16_strcpy(&p, name); -	sprintf(index_buf, "%04X", index); +	snprintf(index_buf, sizeof(index_buf), "%04X", index);  	utf8_utf16_strcpy(&p, index_buf);  	return p; diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c index 62f2f9427b6..797d6eb134f 100644 --- a/lib/efi_loader/efi_tcg2.c +++ b/lib/efi_loader/efi_tcg2.c @@ -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> + +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; +}; -DECLARE_GLOBAL_DATA_PTR; - +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;  	/* @@ -309,6 +468,103 @@ out:  }  /** + * __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   *   * @this:		TCG2 protocol instance @@ -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)  { @@ -533,6 +884,169 @@ static const struct efi_tcg2_protocol efi_tcg2_protocol = {  };  /** + * 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   *   * If a TPM2 device is available, the TPM TCG2 Protocol is registered @@ -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) diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 0c689cfb470..ba0874e9e78 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -24,91 +24,6 @@  #include <asm/sections.h>  #ifdef CONFIG_EFI_SECURE_BOOT -static u8 pkcs7_hdr[] = { -	/* SEQUENCE */ -	0x30, 0x82, 0x05, 0xc7, -	/* OID: pkcs7-signedData */ -	0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, -	/* Context Structured? */ -	0xa0, 0x82, 0x05, 0xb8, -}; - -/** - * efi_variable_parse_signature - parse a signature in variable - * @buf:	Pointer to variable's value - * @buflen:	Length of @buf - * @tmpbuf:	Pointer to temporary buffer - * - * Parse a signature embedded in variable's value and instantiate - * a pkcs7_message structure. Since pkcs7_parse_message() accepts only - * pkcs7's signedData, some header needed be prepended for correctly - * parsing authentication data, particularly for variable's. - * A temporary buffer will be allocated if needed, and it should be - * kept valid during the authentication because some data in the buffer - * will be referenced by efi_signature_verify(). - * - * Return:	Pointer to pkcs7_message structure on success, NULL on error - */ -static struct pkcs7_message *efi_variable_parse_signature(const void *buf, -							  size_t buflen, -							  u8 **tmpbuf) -{ -	u8 *ebuf; -	size_t ebuflen, len; -	struct pkcs7_message *msg; - -	/* -	 * This is the best assumption to check if the binary is -	 * already in a form of pkcs7's signedData. -	 */ -	if (buflen > sizeof(pkcs7_hdr) && -	    !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { -		msg = pkcs7_parse_message(buf, buflen); -		if (IS_ERR(msg)) -			return NULL; -		return msg; -	} - -	/* -	 * Otherwise, we should add a dummy prefix sequence for pkcs7 -	 * message parser to be able to process. -	 * NOTE: EDK2 also uses similar hack in WrapPkcs7Data() -	 * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c -	 * TODO: -	 * The header should be composed in a more refined manner. -	 */ -	EFI_PRINT("Makeshift prefix added to authentication data\n"); -	ebuflen = sizeof(pkcs7_hdr) + buflen; -	if (ebuflen <= 0x7f) { -		EFI_PRINT("Data is too short\n"); -		return NULL; -	} - -	ebuf = malloc(ebuflen); -	if (!ebuf) { -		EFI_PRINT("Out of memory\n"); -		return NULL; -	} - -	memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr)); -	memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen); -	len = ebuflen - 4; -	ebuf[2] = (len >> 8) & 0xff; -	ebuf[3] = len & 0xff; -	len = ebuflen - 0x13; -	ebuf[0x11] = (len >> 8) & 0xff; -	ebuf[0x12] = len & 0xff; - -	msg = pkcs7_parse_message(ebuf, ebuflen); - -	if (IS_ERR(msg)) { -		free(ebuf); -		return NULL; -	} - -	*tmpbuf = ebuf; -	return msg; -}  /**   * efi_variable_authenticate - authenticate a variable @@ -215,10 +130,10 @@ static efi_status_t efi_variable_authenticate(u16 *variable,  		goto err;  	/* ebuf should be kept valid during the authentication */ -	var_sig = efi_variable_parse_signature(auth->auth_info.cert_data, -					       auth->auth_info.hdr.dwLength -						   - sizeof(auth->auth_info), -					       &ebuf); +	var_sig = efi_parse_pkcs7_header(auth->auth_info.cert_data, +					 auth->auth_info.hdr.dwLength +					 - sizeof(auth->auth_info), +					 &ebuf);  	if (!var_sig) {  		EFI_PRINT("Parsing variable's signature failed\n");  		goto err; diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index be6f3dfad46..b8808fdecad 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -36,20 +36,29 @@ static int get_connection(struct mm_connection *conn)  	static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;  	struct udevice *tee = NULL;  	struct tee_open_session_arg arg; -	int rc; +	int rc = -ENODEV;  	tee = tee_find_device(tee, NULL, NULL, NULL);  	if (!tee) -		return -ENODEV; +		goto out;  	memset(&arg, 0, sizeof(arg));  	tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);  	rc = tee_open_session(tee, &arg, 0, NULL); -	if (!rc) { -		conn->tee = tee; -		conn->session = arg.session; +	if (rc) +		goto out; + +	/* Check the internal OP-TEE result */ +	if (arg.ret != TEE_SUCCESS) { +		rc = -EIO; +		goto out;  	} +	conn->tee = tee; +	conn->session = arg.session; + +	return 0; +out:  	return rc;  } @@ -88,6 +97,7 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)  	if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {  		log_err("Unable to register shared memory\n"); +		tee_close_session(conn.tee, conn.session);  		return EFI_UNSUPPORTED;  	} diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 58fb43fcdfc..426552bfa01 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -25,9 +25,12 @@ efi_selftest_crc32.o \  efi_selftest_devicepath_util.o \  efi_selftest_events.o \  efi_selftest_event_groups.o \ +efi_selftest_exception.o \  efi_selftest_exitbootservices.o \  efi_selftest_gop.o \ +efi_selftest_load_file.o \  efi_selftest_loaded_image.o \ +efi_selftest_loadimage.o \  efi_selftest_manageprotocols.o \  efi_selftest_mem.o \  efi_selftest_memory.o \ @@ -35,6 +38,8 @@ efi_selftest_open_protocol.o \  efi_selftest_register_notify.o \  efi_selftest_reset.o \  efi_selftest_set_virtual_address_map.o \ +efi_selftest_startimage_exit.o \ +efi_selftest_startimage_return.o \  efi_selftest_textinput.o \  efi_selftest_textinputex.o \  efi_selftest_textoutput.o \ @@ -65,12 +70,6 @@ ifeq ($(CONFIG_BLK)$(CONFIG_DOS_PARTITION),yy)  obj-y += efi_selftest_block_device.o  endif -obj-y += \ -efi_selftest_exception.o \ -efi_selftest_loadimage.o \ -efi_selftest_startimage_exit.o \ -efi_selftest_startimage_return.o -  targets += \  efi_miniapp_file_image_exception.h \  efi_miniapp_file_image_exit.h \ @@ -94,10 +93,12 @@ $(obj)/efi_miniapp_file_image_return.h: $(obj)/efi_selftest_miniapp_return.efi  	$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_return.efi > \  	$(obj)/efi_miniapp_file_image_return.h -$(obj)/efi_selftest_loadimage.o: $(obj)/efi_miniapp_file_image_exit.h -  $(obj)/efi_selftest_exception.o: $(obj)/efi_miniapp_file_image_exception.h +$(obj)/efi_selftest_load_file.o: $(obj)/efi_miniapp_file_image_exit.h + +$(obj)/efi_selftest_loadimage.o: $(obj)/efi_miniapp_file_image_exit.h +  $(obj)/efi_selftest_startimage_exit.o: $(obj)/efi_miniapp_file_image_exit.h  $(obj)/efi_selftest_startimage_return.o: $(obj)/efi_miniapp_file_image_return.h diff --git a/lib/efi_selftest/efi_selftest_load_file.c b/lib/efi_selftest/efi_selftest_load_file.c new file mode 100644 index 00000000000..4473e7c36ed --- /dev/null +++ b/lib/efi_selftest/efi_selftest_load_file.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_load_file + * + * Copyright (c) 2020 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * This test checks the handling of the LOAD_FILE and the LOAD_FILE2 protocol + * by the LoadImage() service. + */ + +#include <efi_selftest.h> +/* Include containing the miniapp.efi application */ +#include "efi_miniapp_file_image_exit.h" + +/* Block size of compressed disk image */ +#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8 + +/* Binary logarithm of the block size */ +#define LB_BLOCK_SIZE 9 + +#define GUID_VENDOR \ +	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \ +		 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd1) + +#define GUID_VENDOR2 \ +	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \ +		 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd2) + +#define FILE_NAME_SIZE 16 + +static const efi_guid_t efi_st_guid_load_file_protocol = +					EFI_LOAD_FILE_PROTOCOL_GUID; +static const efi_guid_t efi_st_guid_load_file2_protocol = +					EFI_LOAD_FILE2_PROTOCOL_GUID; +static const efi_guid_t efi_st_guid_device_path = +					EFI_DEVICE_PATH_PROTOCOL_GUID; + +static efi_handle_t image_handle; +static struct efi_boot_services *boottime; +static efi_handle_t handle_lf; +static efi_handle_t handle_lf2; + +/* One 8 byte block of the compressed disk image */ +struct line { +	size_t addr; +	char *line; +}; + +/* Compressed file image */ +struct compressed_file_image { +	size_t length; +	struct line lines[]; +}; + +static struct compressed_file_image img = EFI_ST_DISK_IMG; + +static int load_file_call_count; +static int load_file2_call_count; + +/* Decompressed file image */ +static u8 *image; + +static struct { +	struct efi_device_path_vendor v; +	struct efi_device_path d; +} dp_lf_prot = { +	{ +		{ +			DEVICE_PATH_TYPE_HARDWARE_DEVICE, +			DEVICE_PATH_SUB_TYPE_VENDOR, +			sizeof(struct efi_device_path_vendor), +		}, +		GUID_VENDOR, +	}, +	{ +		DEVICE_PATH_TYPE_END, +		DEVICE_PATH_SUB_TYPE_END, +		sizeof(struct efi_device_path), +	}, +}; + +static struct { +	struct efi_device_path_vendor v; +	struct efi_device_path_file_path f; +	u16 file_name[FILE_NAME_SIZE]; +	struct efi_device_path e; +} dp_lf_file = { +	{ +		{ +			DEVICE_PATH_TYPE_HARDWARE_DEVICE, +			DEVICE_PATH_SUB_TYPE_VENDOR, +			sizeof(struct efi_device_path_vendor), +		}, +		GUID_VENDOR, +	}, +	{ +		{ +			DEVICE_PATH_TYPE_MEDIA_DEVICE, +			DEVICE_PATH_SUB_TYPE_FILE_PATH, +			sizeof(struct efi_device_path_file_path) + +			FILE_NAME_SIZE * sizeof(u16), +		} +	}, +	L"\\lf.efi", +	{ +		DEVICE_PATH_TYPE_END, +		DEVICE_PATH_SUB_TYPE_END, +		sizeof(struct efi_device_path), +	}, +}; + +struct efi_device_path *dp_lf_file_remainder = &dp_lf_file.f.dp; + +static struct { +	struct efi_device_path_vendor v; +	struct efi_device_path d; +} dp_lf2_prot = { +	{ +		{ +			DEVICE_PATH_TYPE_HARDWARE_DEVICE, +			DEVICE_PATH_SUB_TYPE_VENDOR, +			sizeof(struct efi_device_path_vendor), +		}, +		GUID_VENDOR2, +	}, +	{ +		DEVICE_PATH_TYPE_END, +		DEVICE_PATH_SUB_TYPE_END, +		sizeof(struct efi_device_path), +	}, +}; + +static struct { +	struct efi_device_path_vendor v; +	struct efi_device_path_file_path f; +	u16 file_name[FILE_NAME_SIZE]; +	struct efi_device_path e; +} dp_lf2_file = { +	{ +		{ +			DEVICE_PATH_TYPE_HARDWARE_DEVICE, +			DEVICE_PATH_SUB_TYPE_VENDOR, +			sizeof(struct efi_device_path_vendor), +		}, +		GUID_VENDOR2, +	}, +	{ +		{ +			DEVICE_PATH_TYPE_MEDIA_DEVICE, +			DEVICE_PATH_SUB_TYPE_FILE_PATH, +			sizeof(struct efi_device_path_file_path) + +			FILE_NAME_SIZE * sizeof(u16), +		} +	}, +	L"\\lf2.efi", +	{ +		DEVICE_PATH_TYPE_END, +		DEVICE_PATH_SUB_TYPE_END, +		sizeof(struct efi_device_path), +	}, +}; + +struct efi_device_path *dp_lf2_file_remainder = &dp_lf2_file.f.dp; + +/* + * Decompress the disk image. + * + * @image	decompressed disk image + * @return	status code + */ +static efi_status_t decompress(u8 **image) +{ +	u8 *buf; +	size_t i; +	size_t addr; +	size_t len; +	efi_status_t ret; + +	ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length, +				      (void **)&buf); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Out of memory\n"); +		return ret; +	} +	boottime->set_mem(buf, img.length, 0); + +	for (i = 0; ; ++i) { +		if (!img.lines[i].line) +			break; +		addr = img.lines[i].addr; +		len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE; +		if (addr + len > img.length) +			len = img.length - addr; +		boottime->copy_mem(buf + addr, img.lines[i].line, len); +	} +	*image = buf; +	return ret; +} + +/* + * load_file() - LoadFile() service of a EFI_LOAD_FILE_PROTOCOL + * + * @this:		instance of EFI_LOAD_FILE_PROTOCOL + * @file_path:		remaining device path + * @boot_policy:	true if called by boot manager + * @buffer_size:	(required) buffer size + * @buffer:		buffer to which the file is to be loaded + */ +efi_status_t EFIAPI load_file(struct efi_load_file_protocol *this, +			      struct efi_device_path *file_path, +			      bool boot_policy, +			      efi_uintn_t *buffer_size, +			      void *buffer) +{ +	++load_file_call_count; +	if (memcmp(file_path, dp_lf_file_remainder, +	    sizeof(struct efi_device_path_file_path) + +	    FILE_NAME_SIZE * sizeof(u16) + +	    sizeof(struct efi_device_path))) { +		efi_st_error("Wrong remaining device path\n"); +		return EFI_NOT_FOUND; +	} +	if (this->load_file != load_file) { +		efi_st_error("wrong this\n"); +		return EFI_INVALID_PARAMETER; +	} +	if (*buffer_size < img.length) { +		*buffer_size = img.length; +		return EFI_BUFFER_TOO_SMALL; +	} +	memcpy(buffer, image, img.length); +	*buffer_size = img.length; +	return EFI_SUCCESS; +} + + +/* + * load_file2() - LoadFile() service of a EFI_LOAD_FILE2_PROTOCOL + * + * @this:		instance of EFI_LOAD_FILE2_PROTOCOL + * @file_path:		remaining device path + * @boot_policy:	true if called by boot manager + * @buffer_size:	(required) buffer size + * @buffer:		buffer to which the file is to be loaded + */ +efi_status_t EFIAPI load_file2(struct efi_load_file_protocol *this, +			       struct efi_device_path *file_path, +			       bool boot_policy, +			       efi_uintn_t *buffer_size, +			       void *buffer) +{ +	++load_file2_call_count; +	if (memcmp(file_path, dp_lf2_file_remainder, +	    sizeof(struct efi_device_path_file_path) + +	    FILE_NAME_SIZE * sizeof(u16) + +	    sizeof(struct efi_device_path))) { +		efi_st_error("Wrong remaining device path\n"); +		return EFI_NOT_FOUND; +	} +	if (this->load_file != load_file2) { +		efi_st_error("wrong this\n"); +		return EFI_INVALID_PARAMETER; +	} +	if (boot_policy) { +		efi_st_error("LOAD_FILE2 called with boot_policy = true"); +		return EFI_INVALID_PARAMETER; +	} +	if (*buffer_size < img.length) { +		*buffer_size = img.length; +		return EFI_BUFFER_TOO_SMALL; +	} +	memcpy(buffer, image, img.length); +	*buffer_size = img.length; +	return EFI_SUCCESS; +} + +static struct efi_load_file_protocol lf_prot = {load_file}; +static struct efi_load_file_protocol lf2_prot = {load_file2}; + +/* + * Setup unit test. + * + * Install an EFI_LOAD_FILE_PROTOCOL and an EFI_LOAD_FILE2_PROTOCOL. + * + * @handle:	handle of the loaded image + * @systable:	system table + * @return:	EFI_ST_SUCCESS for success + */ +static int efi_st_load_file_setup(const efi_handle_t handle, +				  const struct efi_system_table *systable) +{ +	efi_status_t ret; + +	image_handle = handle; +	boottime = systable->boottime; + +	/* Load the application image into memory */ +	decompress(&image); + +	ret = boottime->install_multiple_protocol_interfaces( +		&handle_lf, +		&efi_st_guid_device_path, +		&dp_lf_prot, +		&efi_st_guid_load_file_protocol, +		&lf_prot, +		NULL); +	if (ret != EFI_SUCCESS) { +		efi_st_error("InstallMultipleProtocolInterfaces failed\n"); +		return EFI_ST_FAILURE; +	} +	ret = boottime->install_multiple_protocol_interfaces( +		&handle_lf2, +		&efi_st_guid_device_path, +		&dp_lf2_prot, +		&efi_st_guid_load_file2_protocol, +		&lf2_prot, +		NULL); +	if (ret != EFI_SUCCESS) { +		efi_st_error("InstallMultipleProtocolInterfaces failed\n"); +		return EFI_ST_FAILURE; +	} + +	return EFI_ST_SUCCESS; +} + +/* + * Tear down unit test. + * + * @return:	EFI_ST_SUCCESS for success + */ +static int efi_st_load_file_teardown(void) +{ +	efi_status_t ret = EFI_ST_SUCCESS; + +	if (handle_lf) { +		ret = boottime->uninstall_multiple_protocol_interfaces( +			handle_lf, +			&efi_st_guid_device_path, +			&dp_lf_prot, +			&efi_st_guid_load_file_protocol, +			&lf_prot, +			NULL); +		if (ret != EFI_SUCCESS) { +			efi_st_error( +				"UninstallMultipleProtocolInterfaces failed\n"); +			return EFI_ST_FAILURE; +		} +	} +	if (handle_lf2) { +		ret = boottime->uninstall_multiple_protocol_interfaces( +			handle_lf2, +			&efi_st_guid_device_path, +			&dp_lf2_prot, +			&efi_st_guid_load_file2_protocol, +			&lf2_prot, +			NULL); +		if (ret != EFI_SUCCESS) { +			efi_st_error( +				"UninstallMultipleProtocolInterfaces failed\n"); +			return EFI_ST_FAILURE; +		} +	} + +	if (image) { +		ret = boottime->free_pool(image); +		if (ret != EFI_SUCCESS) { +			efi_st_error("Failed to free image\n"); +			return EFI_ST_FAILURE; +		} +	} +	return ret; +} + +/* + * Execute unit test. + * + * Try loading an image via the EFI_LOAD_FILE_PROTOCOL and the + * EFI_LOAD_FILE2_PROTOCOL. Finally execute the image. + * + * @return:	EFI_ST_SUCCESS for success + */ +static int efi_st_load_file_execute(void) +{ +	efi_status_t ret; +	efi_handle_t handle; +	efi_uintn_t exit_data_size = 0; +	u16 *exit_data = NULL; +	u16 expected_text[] = EFI_ST_SUCCESS_STR; + +	load_file_call_count = 0; +	load_file2_call_count = 0; +	handle = NULL; +	ret = boottime->load_image(true, image_handle, &dp_lf_file.v.dp, NULL, +				   0, &handle); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to load image\n"); +		return EFI_ST_FAILURE; +	} +	if (load_file2_call_count || !load_file_call_count) { +		efi_st_error("Wrong image loaded\n"); +		return EFI_ST_FAILURE; +	} +	ret = boottime->unload_image(handle); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to unload image\n"); +		return EFI_ST_FAILURE; +	} + +	load_file_call_count = 0; +	load_file2_call_count = 0; +	handle = NULL; +	ret = boottime->load_image(false, image_handle, &dp_lf_file.v.dp, NULL, +				   0, &handle); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to load image\n"); +		return EFI_ST_FAILURE; +	} +	if (load_file2_call_count || !load_file_call_count) { +		efi_st_error("Wrong image loaded\n"); +		return EFI_ST_FAILURE; +	} +	ret = boottime->unload_image(handle); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to unload image\n"); +		return EFI_ST_FAILURE; +	} + +	ret = boottime->load_image(true, image_handle, &dp_lf2_file.v.dp, NULL, +				   0, &handle); +	if (ret != EFI_NOT_FOUND) { +		efi_st_error( +			"Boot manager should not use LOAD_FILE2_PROTOCOL\n"); +		return EFI_ST_FAILURE; +	} + +	load_file_call_count = 0; +	load_file2_call_count = 0; +	handle = NULL; +	ret = boottime->load_image(false, image_handle, &dp_lf2_file.v.dp, NULL, +				   0, &handle); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to load image\n"); +		return EFI_ST_FAILURE; +	} +	if (!load_file2_call_count || load_file_call_count) { +		efi_st_error("Wrong image loaded\n"); +		return EFI_ST_FAILURE; +	} + +	ret = boottime->start_image(handle, &exit_data_size, &exit_data); +	if (ret != EFI_UNSUPPORTED) { +		efi_st_error("Wrong return value from application\n"); +		return EFI_ST_FAILURE; +	} +	if (!exit_data || exit_data_size != sizeof(expected_text) || +	    memcmp(exit_data, expected_text, sizeof(expected_text))) { +		efi_st_error("Incorrect exit data\n"); +		return EFI_ST_FAILURE; +	} +	ret = boottime->free_pool(exit_data); +	if (ret != EFI_SUCCESS) { +		efi_st_error("Failed to free exit data\n"); +		return EFI_ST_FAILURE; +	} + +	return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(load_file_protocol) = { +	.name = "load file protocol", +	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, +	.setup = efi_st_load_file_setup, +	.execute = efi_st_load_file_execute, +	.teardown = efi_st_load_file_teardown, +}; diff --git a/lib/efi_selftest/efi_selftest_load_initrd.c b/lib/efi_selftest/efi_selftest_load_initrd.c index fe060a66440..f591dcd2115 100644 --- a/lib/efi_selftest/efi_selftest_load_initrd.c +++ b/lib/efi_selftest/efi_selftest_load_initrd.c @@ -86,7 +86,6 @@ static int setup(const efi_handle_t handle,  static int execute(void)  { -	efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;  	struct efi_load_file_protocol *lf2;  	struct efi_device_path *dp2, *dp2_invalid;  	efi_status_t status; @@ -99,13 +98,15 @@ static int execute(void)  	memset(buffer, 0, sizeof(buffer));  	dp2 = (struct efi_device_path *)&dp; -	status = boottime->locate_device_path(&lf2_proto_guid, &dp2, &handle); +	status = boottime->locate_device_path(&efi_guid_load_file2_protocol, +					      &dp2, &handle);  	if (status != EFI_SUCCESS) {  		efi_st_error("Unable to locate device path\n");  		return EFI_ST_FAILURE;  	} -	status = boottime->handle_protocol(handle, &lf2_proto_guid, +	status = boottime->handle_protocol(handle, +					   &efi_guid_load_file2_protocol,  					   (void **)&lf2);  	if (status != EFI_SUCCESS) {  		efi_st_error("Unable to locate protocol\n"); diff --git a/lib/efi_selftest/efi_selftest_miniapp_exception.c b/lib/efi_selftest/efi_selftest_miniapp_exception.c index 63c63d75f12..59b7e5100ad 100644 --- a/lib/efi_selftest/efi_selftest_miniapp_exception.c +++ b/lib/efi_selftest/efi_selftest_miniapp_exception.c @@ -33,6 +33,8 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,  	asm volatile (".word 0xe7f7defb\n");  #elif defined(CONFIG_RISCV)  	asm volatile (".word 0xffffffff\n"); +#elif defined(CONFIG_SANDBOX) +	asm volatile (".word 0xffffffff\n");  #elif defined(CONFIG_X86)  	asm volatile (".word 0xffff\n");  #endif diff --git a/lib/fdtdec.c b/lib/fdtdec.c index ee1bd41b081..0ab7105fef0 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -500,6 +500,17 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset,  		slash = strrchr(prop, '/');  		if (strcmp(slash + 1, find_name))  			continue; + +		/* +		 * Adding an extra check to distinguish DT nodes with +		 * same name +		 */ +		if (IS_ENABLED(CONFIG_PHANDLE_CHECK_SEQ)) { +			if (fdt_get_phandle(blob, offset) != +			    fdt_get_phandle(blob, fdt_path_offset(blob, prop))) +				continue; +		} +  		val = trailing_strtol(name);  		if (val != -1) {  			*seqp = val; diff --git a/lib/hashtable.c b/lib/hashtable.c index 7c08f5c8055..ff5ff726394 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -472,7 +472,7 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)  	idx = hsearch_r(e, ENV_FIND, &ep, htab, 0);  	if (idx == 0) {  		__set_errno(ESRCH); -		return 0;	/* not found */ +		return -ENOENT;	/* not found */  	}  	/* Check for permission */ @@ -481,7 +481,7 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)  		debug("change_ok() rejected deleting variable "  			"%s, skipping it!\n", key);  		__set_errno(EPERM); -		return 0; +		return -EPERM;  	}  	/* If there is a callback, call it */ @@ -490,12 +490,12 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)  		debug("callback() rejected deleting variable "  			"%s, skipping it!\n", key);  		__set_errno(EINVAL); -		return 0; +		return -EINVAL;  	}  	_hdelete(key, htab, ep, idx); -	return 1; +	return 0;  }  #if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV)) @@ -917,7 +917,7 @@ int himport_r(struct hsearch_data *htab,  			if (!drop_var_from_set(name, nvars, localvars))  				continue; -			if (hdelete_r(name, htab, flag) == 0) +			if (hdelete_r(name, htab, flag))  				debug("DELETE ERROR ##############################\n");  			continue; @@ -979,7 +979,7 @@ int himport_r(struct hsearch_data *htab,  		 * b) if the variable was not present in current env, we notify  		 *    it might be a typo  		 */ -		if (hdelete_r(localvars[i], htab, flag) == 0) +		if (hdelete_r(localvars[i], htab, flag))  			printf("WARNING: '%s' neither in running nor in imported env!\n", localvars[i]);  		else  			printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]); diff --git a/lib/smbios.c b/lib/smbios.c index 485a812c776..1e10fa84207 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -229,7 +229,7 @@ static void smbios_write_type4_dm(struct smbios_type4 *t, ofnode node)  	uclass_find_first_device(UCLASS_CPU, &cpu);  	if (cpu) { -		struct cpu_platdata *plat = dev_get_parent_platdata(cpu); +		struct cpu_plat *plat = dev_get_parent_plat(cpu);  		if (plat->family)  			processor_family = plat->family; diff --git a/lib/time.c b/lib/time.c index 88bc50405ff..cc6944ec345 100644 --- a/lib/time.c +++ b/lib/time.c @@ -9,6 +9,7 @@  #include <dm.h>  #include <errno.h>  #include <init.h> +#include <spl.h>  #include <time.h>  #include <timer.h>  #include <watchdog.h> @@ -96,8 +97,13 @@ uint64_t notrace get_ticks(void)  	}  	ret = timer_get_count(gd->timer, &count); -	if (ret) -		panic("Could not read count from timer (err %d)\n", ret); +	if (ret) { +		if (spl_phase() > PHASE_TPL) +			panic("Could not read count from timer (err %d)\n", +			      ret); +		else +			panic("no timer (err %d)\n", ret); +	}  	return count;  } diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index 91759068cf0..1f3deb06e48 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -80,11 +80,12 @@ u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,  	return tpm_sendrecv_command(dev, command_v2, NULL, NULL);  } -u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest) +u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm, +		    const u8 *digest, u32 digest_len)  {  	u8 command_v2[COMMAND_BUFFER_SIZE] = {  		tpm_u16(TPM2_ST_SESSIONS),	/* TAG */ -		tpm_u32(33 + TPM2_DIGEST_LEN),	/* Length */ +		tpm_u32(33 + digest_len),	/* Length */  		tpm_u32(TPM2_CC_PCR_EXTEND),	/* Command code */  		/* HANDLE */ @@ -99,7 +100,7 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)  		tpm_u16(0),			/* Size of <hmac/password> */  						/* <hmac/password> (if any) */  		tpm_u32(1),			/* Count (number of hashes) */ -		tpm_u16(TPM2_ALG_SHA256),	/* Algorithm of the hash */ +		tpm_u16(algorithm),	/* Algorithm of the hash */  		/* STRING(digest)		   Digest */  	};  	unsigned int offset = 33; @@ -110,8 +111,8 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)  	 *     - the digest  	 */  	ret = pack_byte_string(command_v2, sizeof(command_v2), "s", -			       offset, digest, TPM2_DIGEST_LEN); -	offset += TPM2_DIGEST_LEN; +			       offset, digest, digest_len); +	offset += digest_len;  	if (ret)  		return TPM_LIB_ERROR; | 
