mirror of
				https://git.proxmox.com/git/fwupd
				synced 2025-11-04 05:14:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			755 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			755 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2014-2018 Red Hat, Inc.
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: LGPL-2.1+
 | 
						|
 */
 | 
						|
 | 
						|
#include <efi.h>
 | 
						|
#include <efilib.h>
 | 
						|
 | 
						|
#include "fwup-cleanups.h"
 | 
						|
#include "fwup-common.h"
 | 
						|
#include "fwup-efi.h"
 | 
						|
#include "fwup-debug.h"
 | 
						|
 | 
						|
#define UNUSED __attribute__((__unused__))
 | 
						|
#define GNVN_BUF_SIZE			1024
 | 
						|
#define FWUP_NUM_CAPSULE_UPDATES_MAX	128
 | 
						|
 | 
						|
typedef struct {
 | 
						|
	CHAR16			*name;
 | 
						|
	UINT32			 attrs;
 | 
						|
	UINTN			 size;
 | 
						|
	FWUP_UPDATE_INFO	*info;
 | 
						|
} FWUP_UPDATE_TABLE;
 | 
						|
 | 
						|
static VOID
 | 
						|
fwup_update_table_free(FWUP_UPDATE_TABLE *update)
 | 
						|
{
 | 
						|
	FreePool(update->info);
 | 
						|
	FreePool(update->name);
 | 
						|
	FreePool(update);
 | 
						|
}
 | 
						|
 | 
						|
_DEFINE_CLEANUP_FUNCTION0(FWUP_UPDATE_TABLE *, _fwup_update_table_free_p, fwup_update_table_free)
 | 
						|
#define _cleanup_update_table __attribute__ ((cleanup(_fwup_update_table_free_p)))
 | 
						|
 | 
						|
#define SECONDS 1000000
 | 
						|
 | 
						|
static INTN
 | 
						|
fwup_dp_size(EFI_DEVICE_PATH *dp, INTN limit)
 | 
						|
{
 | 
						|
	INTN ret = 0;
 | 
						|
	while (1) {
 | 
						|
		if (limit < 4)
 | 
						|
			break;
 | 
						|
		INTN nodelen = DevicePathNodeLength(dp);
 | 
						|
		if (nodelen > limit)
 | 
						|
			break;
 | 
						|
		limit -= nodelen;
 | 
						|
		ret += nodelen;
 | 
						|
 | 
						|
		if (IsDevicePathEnd(dp))
 | 
						|
			return ret;
 | 
						|
		dp = NextDevicePathNode(dp);
 | 
						|
	}
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_populate_update_info(CHAR16 *name, FWUP_UPDATE_TABLE *info_out)
 | 
						|
{
 | 
						|
	EFI_STATUS rc;
 | 
						|
	FWUP_UPDATE_INFO *info = NULL;
 | 
						|
	UINTN info_size = 0;
 | 
						|
	UINT32 attrs = 0;
 | 
						|
	VOID *info_ptr = NULL;
 | 
						|
 | 
						|
	rc = fwup_get_variable(name, &fwupdate_guid, &info_ptr, &info_size, &attrs);
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		return rc;
 | 
						|
	info = (FWUP_UPDATE_INFO *)info_ptr;
 | 
						|
 | 
						|
	if (info_size < sizeof(*info)) {
 | 
						|
		fwup_warning(L"Update '%s' is is too small", name);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	if (info_size - sizeof(EFI_DEVICE_PATH) <= sizeof(*info)) {
 | 
						|
		fwup_warning(L"Update '%s' is malformed, "
 | 
						|
			     L"and cannot hold a file path", name);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp;
 | 
						|
	INTN is = EFI_FIELD_OFFSET(FWUP_UPDATE_INFO, dp);
 | 
						|
	if (is > (INTN)info_size) {
 | 
						|
		fwup_warning(L"Update '%s' has an invalid file path, "
 | 
						|
			     L"device path offset is %d, but total size is %d",
 | 
						|
			     name, is, info_size);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	is = info_size - is;
 | 
						|
	INTN sz = fwup_dp_size(hdr, info_size);
 | 
						|
	if (sz < 0 || is > (INTN)info_size || is != sz) {
 | 
						|
		fwup_warning(L"Update '%s' has an invalid file path, "
 | 
						|
			     L"update info size: %d dp size: %d size for dp: %d",
 | 
						|
			     name, info_size, sz, is);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	info_out->info = info;
 | 
						|
	info_out->size = info_size;
 | 
						|
	info_out->attrs = attrs;
 | 
						|
	info_out->name = StrDuplicate(name);
 | 
						|
	if (info_out->name == NULL) {
 | 
						|
		fwup_warning(L"Could not allocate %d", StrSize(name));
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
	}
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_populate_update_table(FWUP_UPDATE_TABLE **updates, UINTN *n_updates_out)
 | 
						|
{
 | 
						|
	EFI_GUID vendor_guid = empty_guid;
 | 
						|
	EFI_STATUS rc;
 | 
						|
	UINTN n_updates = 0;
 | 
						|
	_cleanup_free CHAR16 *variable_name = NULL;
 | 
						|
 | 
						|
	/* How much do we trust "size of the VariableName buffer" to mean
 | 
						|
	 * sizeof(vn) and not sizeof(vn)/sizeof(vn[0]) ? */
 | 
						|
	variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2);
 | 
						|
	if (variable_name == NULL)
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
 | 
						|
	while (1) {
 | 
						|
		UINTN variable_name_size = GNVN_BUF_SIZE;
 | 
						|
		rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
 | 
						|
				       &variable_name_size, variable_name,
 | 
						|
				       &vendor_guid);
 | 
						|
		if (rc == EFI_NOT_FOUND)
 | 
						|
			break;
 | 
						|
 | 
						|
		/* ignore any huge names */
 | 
						|
		if (rc == EFI_BUFFER_TOO_SMALL)
 | 
						|
			continue;
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			fwup_warning(L"Could not get variable name: %r", rc);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
 | 
						|
		/* not one of our state variables */
 | 
						|
		if (CompareGuid(&vendor_guid, &fwupdate_guid))
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* ignore debugging settings */
 | 
						|
		if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 ||
 | 
						|
		    StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (n_updates > FWUP_NUM_CAPSULE_UPDATES_MAX) {
 | 
						|
			fwup_warning(L"Ignoring update %s", variable_name);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		fwup_info(L"Found update %s", variable_name);
 | 
						|
		_cleanup_update_table FWUP_UPDATE_TABLE *update = fwup_malloc0(sizeof(FWUP_UPDATE_TABLE));
 | 
						|
		if (update == NULL)
 | 
						|
			return EFI_OUT_OF_RESOURCES;
 | 
						|
		rc = fwup_populate_update_info(variable_name, update);
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			fwup_delete_variable(variable_name, &fwupdate_guid);
 | 
						|
			fwup_warning(L"Could not populate update info for '%s'", variable_name);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		if (update->info->status & FWUPDATE_ATTEMPT_UPDATE) {
 | 
						|
			fwup_time(&update->info->time_attempted);
 | 
						|
			update->info->status = FWUPDATE_ATTEMPTED;
 | 
						|
			updates[n_updates++] = _steal_pointer(&update);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	*n_updates_out = n_updates;
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh)
 | 
						|
{
 | 
						|
	EFI_DEVICE_PATH *dp, *parent_dp;
 | 
						|
	EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL;
 | 
						|
	EFI_GUID dpp = DEVICE_PATH_PROTOCOL;
 | 
						|
	UINTN n_handles, count;
 | 
						|
	EFI_STATUS rc;
 | 
						|
	_cleanup_free EFI_FILE_HANDLE *devices = NULL;
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &sfsp,
 | 
						|
			       NULL, &n_handles, (EFI_HANDLE **)&devices);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not find handles");
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	dp = *file_dp;
 | 
						|
 | 
						|
	fwup_debug(L"Searching Device Path: %s...", DevicePathToStr(dp));
 | 
						|
	parent_dp = DuplicateDevicePath(dp);
 | 
						|
	if (parent_dp == NULL)
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
 | 
						|
	dp = parent_dp;
 | 
						|
	count = 0;
 | 
						|
	while (1) {
 | 
						|
		if (IsDevicePathEnd(dp))
 | 
						|
			return EFI_INVALID_PARAMETER;
 | 
						|
 | 
						|
		if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
 | 
						|
		    DevicePathSubType(dp) == MEDIA_FILEPATH_DP)
 | 
						|
			break;
 | 
						|
 | 
						|
		dp = NextDevicePathNode(dp);
 | 
						|
		++count;
 | 
						|
	}
 | 
						|
 | 
						|
	SetDevicePathEndNode(dp);
 | 
						|
	fwup_debug(L"Device Path prepared: %s", DevicePathToStr(parent_dp));
 | 
						|
 | 
						|
	for (UINTN i = 0; i < n_handles; i++) {
 | 
						|
		EFI_DEVICE_PATH *path;
 | 
						|
 | 
						|
		rc = uefi_call_wrapper(BS->HandleProtocol, 3, devices[i], &dpp,
 | 
						|
				       (VOID **)&path);
 | 
						|
		if (EFI_ERROR(rc))
 | 
						|
			continue;
 | 
						|
 | 
						|
		fwup_debug(L"Device supporting SFSP: %s", DevicePathToStr(path));
 | 
						|
 | 
						|
		while (!IsDevicePathEnd(path)) {
 | 
						|
			fwup_debug(L"Comparing: %s and %s",
 | 
						|
			           DevicePathToStr(parent_dp),
 | 
						|
			           DevicePathToStr(path));
 | 
						|
 | 
						|
			if (LibMatchDevicePaths(path, parent_dp) == TRUE) {
 | 
						|
				*fh = devices[i];
 | 
						|
				for (UINTN j = 0; j < count; j++)
 | 
						|
					*file_dp = NextDevicePathNode(*file_dp);
 | 
						|
 | 
						|
				fwup_debug(L"Match up! Returning %s",
 | 
						|
					   DevicePathToStr(*file_dp));
 | 
						|
				return EFI_SUCCESS;
 | 
						|
			}
 | 
						|
 | 
						|
			path = NextDevicePathNode(path);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fwup_warning(L"Failed to find '%s' DevicePath", DevicePathToStr(*file_dp));
 | 
						|
	return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh)
 | 
						|
{
 | 
						|
	CONST UINTN devpath_max_size = 1024; /* arbitrary limit */
 | 
						|
	EFI_DEVICE_PATH *file_dp = dp;
 | 
						|
	EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL;
 | 
						|
	EFI_FILE_HANDLE device;
 | 
						|
	EFI_FILE_IO_INTERFACE *drive;
 | 
						|
	EFI_FILE_HANDLE root;
 | 
						|
	EFI_STATUS rc;
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(BS->LocateDevicePath, 3, &sfsp, &file_dp,
 | 
						|
			       (EFI_HANDLE *)&device);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		rc = fwup_search_file(&file_dp, &device);
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			fwup_warning(L"Could not locate device handle: %r", rc);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH ||
 | 
						|
			   DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) {
 | 
						|
		fwup_warning(L"Could not find appropriate device");
 | 
						|
		return EFI_UNSUPPORTED;
 | 
						|
	}
 | 
						|
 | 
						|
	UINT16 sz16;
 | 
						|
	UINTN sz;
 | 
						|
	CopyMem(&sz16, &file_dp->Length[0], sizeof(sz16));
 | 
						|
	sz = sz16;
 | 
						|
	sz -= 4;
 | 
						|
	if (sz <= 6 || sz % 2 != 0 ||
 | 
						|
	    sz > devpath_max_size * sizeof(CHAR16)) {
 | 
						|
		fwup_warning(L"Invalid file device path of size %d", sz);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	_cleanup_free CHAR16 *filename = fwup_malloc0(sz + sizeof(CHAR16));
 | 
						|
	CopyMem(filename, (UINT8 *)file_dp + 4, sz);
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, &sfsp,
 | 
						|
			       (VOID **)&drive);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not open device interface: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	fwup_debug(L"Found device");
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not open volume: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	fwup_debug(L"Found volume");
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(root->Open, 5, root, fh, filename,
 | 
						|
			       EFI_FILE_MODE_READ, 0);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not open file '%s': %r", filename, rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	fwup_debug(L"Found file");
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_delete_boot_order(CHAR16 *name, EFI_GUID guid)
 | 
						|
{
 | 
						|
	UINT16 boot_num;
 | 
						|
	EFI_STATUS rc;
 | 
						|
	UINTN info_size = 0;
 | 
						|
	UINT32 attrs = 0;
 | 
						|
	_cleanup_free VOID *info_ptr = NULL;
 | 
						|
	_cleanup_free UINT16 *new_info_ptr = NULL;
 | 
						|
	UINT8 num_found = FALSE;
 | 
						|
	UINTN new_list_num = 0;
 | 
						|
 | 
						|
	/* get boot hex number */
 | 
						|
	boot_num = xtoi((CHAR16 *)((UINT8 *)name + sizeof(L"Boot")));
 | 
						|
 | 
						|
	rc = fwup_get_variable(L"BootOrder", &guid, &info_ptr, &info_size, &attrs);
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		return rc;
 | 
						|
 | 
						|
	new_info_ptr = fwup_malloc(info_size);
 | 
						|
	if (new_info_ptr == NULL)
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
 | 
						|
	for (UINTN i = 0; i < (info_size / sizeof(UINT16)) ; i++) {
 | 
						|
		if (((UINT16 *)info_ptr)[i] != boot_num) {
 | 
						|
			new_info_ptr[i] = ((UINT16 *)info_ptr)[i];
 | 
						|
			new_list_num++;
 | 
						|
 | 
						|
		} else {
 | 
						|
			num_found = TRUE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* if not in the BootOrder list, do not update BootOrder */
 | 
						|
	if (!num_found)
 | 
						|
		return EFI_SUCCESS;
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid,
 | 
						|
				attrs, new_list_num * sizeof(UINT16),
 | 
						|
				new_info_ptr);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not update variable status for '%s': %r",
 | 
						|
			     name, rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
/* TODO: move to gnu-efi: https://github.com/vathpela/gnu-efi/issues/7 */
 | 
						|
static BOOLEAN
 | 
						|
_StrHasPrefix(IN CONST CHAR16 *s1, IN CONST CHAR16 *s2)
 | 
						|
{
 | 
						|
	while (*s2) {
 | 
						|
		if (*s1 == L'\0' || *s1 != *s2)
 | 
						|
			return FALSE;
 | 
						|
		s1  += 1;
 | 
						|
		s2  += 1;
 | 
						|
	}
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_delete_boot_entry(VOID)
 | 
						|
{
 | 
						|
	EFI_STATUS rc;
 | 
						|
	UINTN variable_name_size = 0;
 | 
						|
	_cleanup_free CHAR16 *variable_name = NULL;
 | 
						|
	EFI_GUID vendor_guid = empty_guid;
 | 
						|
 | 
						|
	variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2);
 | 
						|
	if (variable_name == NULL)
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
 | 
						|
	while (1) {
 | 
						|
		variable_name_size = GNVN_BUF_SIZE;
 | 
						|
		rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
 | 
						|
				       &variable_name_size, variable_name,
 | 
						|
				       &vendor_guid);
 | 
						|
		if (rc == EFI_NOT_FOUND)
 | 
						|
			break;
 | 
						|
		/* ignore any huge names */
 | 
						|
		if (rc == EFI_BUFFER_TOO_SMALL)
 | 
						|
			continue;
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			fwup_warning(L"Could not get variable name: %r", rc);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
 | 
						|
		/* check if the variable name is Boot#### */
 | 
						|
		if (CompareGuid(&vendor_guid, &global_variable_guid) != 0)
 | 
						|
			continue;
 | 
						|
		if (StrCmp(variable_name, L"Boot") != 0)
 | 
						|
			continue;
 | 
						|
 | 
						|
		UINTN info_size = 0;
 | 
						|
		_cleanup_free VOID *info_ptr = NULL;
 | 
						|
 | 
						|
		/* get the data */
 | 
						|
		rc = fwup_get_variable(variable_name, &vendor_guid,
 | 
						|
				       &info_ptr, &info_size, NULL);
 | 
						|
		if (EFI_ERROR(rc))
 | 
						|
			return rc;
 | 
						|
		if (info_size < sizeof(EFI_LOAD_OPTION))
 | 
						|
			continue;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * check if the boot path created by fwupdate,
 | 
						|
		 * check with EFI_LOAD_OPTION description
 | 
						|
		 */
 | 
						|
		EFI_LOAD_OPTION *load_op = (EFI_LOAD_OPTION *) info_ptr;
 | 
						|
		if (_StrHasPrefix(load_op->description, L"Linux Firmware Updater") ||
 | 
						|
		    _StrHasPrefix(load_op->description, L"Linux-Firmware-Updater")) {
 | 
						|
			/* delete the boot path from BootOrder list */
 | 
						|
			rc = fwup_delete_boot_order(variable_name, vendor_guid);
 | 
						|
			if (EFI_ERROR(rc)) {
 | 
						|
				fwup_warning(L"Failed to delete boot entry from BootOrder");
 | 
						|
				return rc;
 | 
						|
			}
 | 
						|
			rc = fwup_delete_variable(variable_name, &vendor_guid);
 | 
						|
			if (EFI_ERROR(rc)) {
 | 
						|
				fwup_warning(L"Failed to delete boot entry");
 | 
						|
				return rc;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image)
 | 
						|
{
 | 
						|
	EFI_HANDLE *handles, gop_handle;
 | 
						|
	UINTN num_handles;
 | 
						|
	EFI_STATUS status;
 | 
						|
	EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
 | 
						|
	EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
 | 
						|
	VOID *iface;
 | 
						|
 | 
						|
	status = LibLocateHandle(ByProtocol, &gop_guid, NULL, &num_handles,
 | 
						|
				 &handles);
 | 
						|
	if (EFI_ERROR(status))
 | 
						|
		return status;
 | 
						|
 | 
						|
	if (handles == NULL || num_handles == 0)
 | 
						|
		return EFI_UNSUPPORTED;
 | 
						|
 | 
						|
	for (UINTN i = 0; i < num_handles; i++) {
 | 
						|
		gop_handle = handles[i];
 | 
						|
 | 
						|
		status = uefi_call_wrapper(BS->OpenProtocol, 6,
 | 
						|
					   gop_handle, &gop_guid, &iface,
 | 
						|
					   loaded_image, 0,
 | 
						|
					   EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | 
						|
		if (EFI_ERROR(status))
 | 
						|
		    continue;
 | 
						|
 | 
						|
		gop = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)iface;
 | 
						|
 | 
						|
		*mode = gop->Mode->Mode;
 | 
						|
		return EFI_SUCCESS;
 | 
						|
	}
 | 
						|
 | 
						|
	return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
fwup_update_ux_capsule_checksum(UX_CAPSULE_HEADER *payload_hdr)
 | 
						|
{
 | 
						|
	UINT8 *buf = (UINT8 *)payload_hdr;
 | 
						|
	UINT8 sum = 0;
 | 
						|
 | 
						|
	payload_hdr->checksum = 0;
 | 
						|
	for (UINTN i = 0; i < sizeof(*payload_hdr); i++)
 | 
						|
		sum = (UINT8) (sum + buf[i]);
 | 
						|
	payload_hdr->checksum = sum;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_check_gop_for_ux_capsule(EFI_HANDLE loaded_image,
 | 
						|
			      EFI_CAPSULE_HEADER *capsule)
 | 
						|
{
 | 
						|
	UX_CAPSULE_HEADER *payload_hdr;
 | 
						|
	EFI_STATUS rc;
 | 
						|
 | 
						|
	payload_hdr = (UX_CAPSULE_HEADER *) (((UINT8 *) capsule) + capsule->HeaderSize);
 | 
						|
	rc = fwup_get_gop_mode(&payload_hdr->mode, loaded_image);
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		return EFI_UNSUPPORTED;
 | 
						|
 | 
						|
	fwup_update_ux_capsule_checksum(payload_hdr);
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_add_update_capsule(FWUP_UPDATE_TABLE *update, EFI_CAPSULE_HEADER **capsule_out,
 | 
						|
			EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out, EFI_HANDLE loaded_image)
 | 
						|
{
 | 
						|
	EFI_STATUS rc;
 | 
						|
	EFI_FILE_HANDLE fh = NULL;
 | 
						|
	UINT8 *fbuf = NULL;
 | 
						|
	UINTN fsize = 0;
 | 
						|
	EFI_CAPSULE_HEADER *capsule;
 | 
						|
 | 
						|
	UINTN cbd_len;
 | 
						|
	EFI_PHYSICAL_ADDRESS cbd_data;
 | 
						|
	EFI_CAPSULE_HEADER *cap_out;
 | 
						|
 | 
						|
	rc = fwup_open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh);
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		return rc;
 | 
						|
 | 
						|
	rc = fwup_read_file(fh, &fbuf, &fsize);
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		return rc;
 | 
						|
 | 
						|
	uefi_call_wrapper(fh->Close, 1, fh);
 | 
						|
 | 
						|
	if (fsize < sizeof(EFI_CAPSULE_HEADER)) {
 | 
						|
		fwup_warning(L"Invalid capsule size %d", fsize);
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	fwup_debug(L"Read file; %d bytes", fsize);
 | 
						|
	fwup_debug(L"updates guid: %g", &update->info->guid);
 | 
						|
	fwup_debug(L"File guid: %g", fbuf);
 | 
						|
 | 
						|
	cbd_len = fsize;
 | 
						|
	cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf;
 | 
						|
	capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf;
 | 
						|
	if (cap_out->Flags == 0 &&
 | 
						|
	    CompareGuid(&update->info->guid, &ux_capsule_guid) != 0) {
 | 
						|
#if defined(__aarch64__)
 | 
						|
		cap_out->Flags |= update->info->capsule_flags;
 | 
						|
#else
 | 
						|
		cap_out->Flags |= update->info->capsule_flags |
 | 
						|
					CAPSULE_FLAGS_PERSIST_ACROSS_RESET |
 | 
						|
					CAPSULE_FLAGS_INITIATE_RESET;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if (CompareGuid(&update->info->guid, &ux_capsule_guid) == 0) {
 | 
						|
		fwup_debug(L"Checking GOP for ux capsule");
 | 
						|
		rc = fwup_check_gop_for_ux_capsule(loaded_image, capsule);
 | 
						|
		if (EFI_ERROR(rc))
 | 
						|
			return EFI_UNSUPPORTED;
 | 
						|
	}
 | 
						|
 | 
						|
	cbd_out->Length = cbd_len;
 | 
						|
	cbd_out->Union.DataBlock = cbd_data;
 | 
						|
	*capsule_out = cap_out;
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_apply_capsules(EFI_CAPSULE_HEADER **capsules,
 | 
						|
		    EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd,
 | 
						|
		    UINTN num_updates, EFI_RESET_TYPE *reset)
 | 
						|
{
 | 
						|
	UINT64 max_capsule_size;
 | 
						|
	EFI_STATUS rc;
 | 
						|
 | 
						|
	/* still try to apply capsule on failure */
 | 
						|
	rc = fwup_delete_boot_entry();
 | 
						|
	if (EFI_ERROR(rc))
 | 
						|
		fwup_warning(L"Could not delete boot entry: %r", rc);
 | 
						|
 | 
						|
	rc = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules,
 | 
						|
				num_updates, &max_capsule_size, reset);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not query capsule capabilities: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	fwup_debug(L"QueryCapsuleCapabilities: %r max: %ld reset:%d",
 | 
						|
	           rc, max_capsule_size, *reset);
 | 
						|
	fwup_debug(L"Capsules: %d", num_updates);
 | 
						|
 | 
						|
	fwup_msleep(1 * SECONDS);
 | 
						|
	rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates,
 | 
						|
			       (EFI_PHYSICAL_ADDRESS)(UINTN)cbd);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not apply capsule update: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS
 | 
						|
fwup_set_update_statuses(FWUP_UPDATE_TABLE **updates)
 | 
						|
{
 | 
						|
	EFI_STATUS rc;
 | 
						|
	for (UINTN i = 0; i < FWUP_NUM_CAPSULE_UPDATES_MAX; i++) {
 | 
						|
		if (updates[i] == NULL || updates[i]->name == NULL)
 | 
						|
			break;
 | 
						|
		rc = fwup_set_variable(updates[i]->name, &fwupdate_guid,
 | 
						|
				       updates[i]->info, updates[i]->size,
 | 
						|
				       updates[i]->attrs);
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			fwup_warning(L"Could not update variable status for '%s': %r",
 | 
						|
				     updates[i]->name, rc);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
EFI_GUID SHIM_LOCK_GUID =
 | 
						|
 {0x605dab50,0xe046,0x4300,{0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23}};
 | 
						|
 | 
						|
static VOID
 | 
						|
__attribute__((__optimize__("0")))
 | 
						|
fwup_debug_hook(VOID)
 | 
						|
{
 | 
						|
	EFI_GUID guid = SHIM_LOCK_GUID;
 | 
						|
	UINTN data = 0;
 | 
						|
	UINTN data_size = 1;
 | 
						|
	EFI_STATUS efi_status;
 | 
						|
	UINT32 attrs;
 | 
						|
	register volatile int x = 0;
 | 
						|
	extern char _text UNUSED, _data UNUSED;
 | 
						|
 | 
						|
	/* shim has done whatever is needed to get a debugger attached */
 | 
						|
	efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG",
 | 
						|
				       &guid, &attrs,  &data_size, &data);
 | 
						|
	if (EFI_ERROR(efi_status) || data != 1) {
 | 
						|
		efi_status = uefi_call_wrapper(RT->GetVariable, 5,
 | 
						|
					       L"FWUPDATE_VERBOSE",
 | 
						|
					       &fwupdate_guid, &attrs,
 | 
						|
					       &data_size, &data);
 | 
						|
		if (EFI_ERROR(efi_status) || data != 1)
 | 
						|
			return;
 | 
						|
		fwup_debug_set_enabled(TRUE);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	fwup_debug_set_enabled(TRUE);
 | 
						|
	if (x)
 | 
						|
		return;
 | 
						|
 | 
						|
	x = 1;
 | 
						|
	fwup_info(L"add-symbol-file "DEBUGDIR
 | 
						|
		  L"fwupdate.efi.debug %p -s .data %p",
 | 
						|
		  &_text, &_data);
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
 | 
						|
{
 | 
						|
	EFI_STATUS rc;
 | 
						|
	UINTN i, n_updates = 0;
 | 
						|
	EFI_RESET_TYPE reset_type = EfiResetWarm;
 | 
						|
	_cleanup_free FWUP_UPDATE_TABLE **updates = NULL;
 | 
						|
 | 
						|
	InitializeLib(image, systab);
 | 
						|
 | 
						|
	/* if SHIM_DEBUG is set, fwup_info info for our attached debugger */
 | 
						|
	fwup_debug_hook();
 | 
						|
 | 
						|
	/* step 1: find and validate update state variables */
 | 
						|
	/* XXX TODO:
 | 
						|
	 * 1) survey the reset types first, and separate into groups
 | 
						|
	 *    according to them
 | 
						|
	 * 2) if there's more than one, mirror BootCurrent back into BootNext
 | 
						|
	 *    so we can do multiple runs
 | 
						|
	 * 3) only select the ones from one type for the first go
 | 
						|
	 */
 | 
						|
	updates = fwup_new0(FWUP_UPDATE_TABLE *, FWUP_NUM_CAPSULE_UPDATES_MAX);
 | 
						|
	if (updates == NULL)
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
	rc = fwup_populate_update_table(updates, &n_updates);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not find updates: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	if (n_updates == 0) {
 | 
						|
		fwup_warning(L"No updates to process.  Called in error?");
 | 
						|
		return EFI_INVALID_PARAMETER;
 | 
						|
	}
 | 
						|
 | 
						|
	/* step 2: Build our data structure and add the capsules to it */
 | 
						|
	_cleanup_free EFI_CAPSULE_HEADER **capsules = NULL;
 | 
						|
	capsules = fwup_new0(EFI_CAPSULE_HEADER *, n_updates + 1);
 | 
						|
	EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data;
 | 
						|
	UINTN j = 0;
 | 
						|
	cbd_data = fwup_malloc_raw(sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1));
 | 
						|
	if (cbd_data == NULL)
 | 
						|
		return EFI_OUT_OF_RESOURCES;
 | 
						|
	for (i = 0; i < n_updates; i++) {
 | 
						|
		fwup_info(L"Adding new capsule");
 | 
						|
		rc = fwup_add_update_capsule(updates[i], &capsules[j], &cbd_data[j], image);
 | 
						|
		if (EFI_ERROR(rc)) {
 | 
						|
			/* ignore a failing UX capsule */
 | 
						|
			if (rc == EFI_UNSUPPORTED &&
 | 
						|
			    CompareGuid(&updates[i]->info->guid, &ux_capsule_guid) == 0) {
 | 
						|
				fwup_debug(L"GOP unsuitable: %r", rc);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			fwup_warning(L"Could not build update list: %r", rc);
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		j++;
 | 
						|
	}
 | 
						|
	n_updates = j;
 | 
						|
	fwup_debug(L"n_updates: %d", n_updates);
 | 
						|
 | 
						|
	cbd_data[i].Length = 0;
 | 
						|
	cbd_data[i].Union.ContinuationPointer = 0;
 | 
						|
 | 
						|
	/* step 3: update the state variables */
 | 
						|
	rc = fwup_set_update_statuses(updates);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not set update status: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	/* step 4: apply the capsules */
 | 
						|
	rc = fwup_apply_capsules(capsules, cbd_data, n_updates, &reset_type);
 | 
						|
	if (EFI_ERROR(rc)) {
 | 
						|
		fwup_warning(L"Could not apply capsules: %r", rc);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	/* step 5: if #4 didn't reboot us, do it manually */
 | 
						|
	fwup_info(L"Reset System");
 | 
						|
	fwup_msleep(5 * SECONDS);
 | 
						|
	if (fwup_debug_get_enabled())
 | 
						|
		fwup_msleep(30 * SECONDS);
 | 
						|
	uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS, 0, NULL);
 | 
						|
 | 
						|
	return EFI_SUCCESS;
 | 
						|
}
 |