1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * EFI Capsule
- *
- * Copyright (c) 2018 Linaro Limited
- * Author: AKASHI Takahiro
- */
- #define LOG_CATEGORY LOGC_EFI
- #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[5];
- efi_uintn_t size;
- unsigned long index = 0xffff;
- efi_status_t ret;
- int i;
- size = sizeof(value16);
- ret = efi_get_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
- NULL, &size, value16, NULL);
- if (ret != EFI_SUCCESS || size != 22 ||
- u16_strncmp(value16, L"Capsule", 7))
- goto err;
- for (i = 0; i < 4; ++i) {
- u16 c = value16[i + 7];
- if (!c || c > 0x7f)
- goto err;
- value[i] = c;
- }
- value[4] = 0;
- if (strict_strtoul(value, 16, &index))
- index = 0xffff;
- 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_int(variable_name16, &efi_guid_capsule_report,
- EFI_VARIABLE_NON_VOLATILE |
- EFI_VARIABLE_BOOTSERVICE_ACCESS |
- EFI_VARIABLE_RUNTIME_ACCESS,
- sizeof(result), &result, false);
- if (ret != EFI_SUCCESS) {
- log_err("Setting %ls failed\n", variable_name16);
- return;
- }
- /* Variable CapsuleLast must not include terminating 0x0000 */
- ret = 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);
- if (ret != EFI_SUCCESS)
- log_err("Setting %ls failed\n", L"CapsuleLast");
- }
- #ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
- /**
- * efi_fmp_find - search for Firmware Management Protocol drivers
- * @image_type: Image type guid
- * @instance: Instance number
- * @handles: Handles of FMP drivers
- * @no_handles: Number of handles
- *
- * Search for Firmware Management Protocol drivers, matching the image
- * type, @image_type and the machine instance, @instance, from the list,
- * @handles.
- *
- * Return:
- * * Protocol instance - on success
- * * NULL - on failure
- */
- static struct efi_firmware_management_protocol *
- efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles,
- efi_uintn_t no_handles)
- {
- efi_handle_t *handle;
- struct efi_firmware_management_protocol *fmp;
- struct efi_firmware_image_descriptor *image_info, *desc;
- efi_uintn_t info_size, descriptor_size;
- u32 descriptor_version;
- u8 descriptor_count;
- u32 package_version;
- u16 *package_version_name;
- bool found = false;
- int i, j;
- efi_status_t ret;
- for (i = 0, handle = handles; i < no_handles; i++, handle++) {
- ret = EFI_CALL(efi_handle_protocol(
- *handle,
- &efi_guid_firmware_management_protocol,
- (void **)&fmp));
- if (ret != EFI_SUCCESS)
- continue;
- /* get device's image info */
- info_size = 0;
- image_info = NULL;
- descriptor_version = 0;
- descriptor_count = 0;
- descriptor_size = 0;
- package_version = 0;
- package_version_name = NULL;
- ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
- image_info,
- &descriptor_version,
- &descriptor_count,
- &descriptor_size,
- &package_version,
- &package_version_name));
- if (ret != EFI_BUFFER_TOO_SMALL)
- goto skip;
- image_info = malloc(info_size);
- if (!image_info)
- goto skip;
- ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
- image_info,
- &descriptor_version,
- &descriptor_count,
- &descriptor_size,
- &package_version,
- &package_version_name));
- if (ret != EFI_SUCCESS ||
- descriptor_version != EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION)
- goto skip;
- /* matching */
- for (j = 0, desc = image_info; j < descriptor_count;
- j++, desc = (void *)desc + descriptor_size) {
- log_debug("+++ desc[%d] index: %d, name: %ls\n",
- j, desc->image_index, desc->image_id_name);
- if (!guidcmp(&desc->image_type_id, image_type) &&
- (!instance ||
- !desc->hardware_instance ||
- desc->hardware_instance == instance))
- found = true;
- }
- skip:
- efi_free_pool(package_version_name);
- free(image_info);
- EFI_CALL(efi_close_protocol(
- (efi_handle_t)fmp,
- &efi_guid_firmware_management_protocol,
- NULL, NULL));
- if (found)
- return fmp;
- }
- return NULL;
- }
- /**
- * efi_remove_auth_hdr - remove authentication data from image
- * @image: Pointer to pointer to Image
- * @image_size: Pointer to Image size
- *
- * Remove the authentication data from image if possible.
- * Update @image and @image_size.
- *
- * Return: status code
- */
- static efi_status_t efi_remove_auth_hdr(void **image, efi_uintn_t *image_size)
- {
- struct efi_firmware_image_authentication *auth_hdr;
- efi_status_t ret = EFI_INVALID_PARAMETER;
- auth_hdr = (struct efi_firmware_image_authentication *)*image;
- if (*image_size < sizeof(*auth_hdr))
- goto out;
- if (auth_hdr->auth_info.hdr.dwLength <=
- offsetof(struct win_certificate_uefi_guid, cert_data))
- goto out;
- *image = (uint8_t *)*image + sizeof(auth_hdr->monotonic_count) +
- auth_hdr->auth_info.hdr.dwLength;
- *image_size = *image_size - auth_hdr->auth_info.hdr.dwLength -
- sizeof(auth_hdr->monotonic_count);
- ret = EFI_SUCCESS;
- out:
- return ret;
- }
- #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- 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;
- *image = (uint8_t *)capsule;
- *image_size = capsule_size;
- if (efi_remove_auth_hdr(image, image_size) != EFI_SUCCESS)
- goto out;
- auth_hdr = (struct efi_firmware_image_authentication *)capsule;
- if (guidcmp(&auth_hdr->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
- goto out;
- 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, image_binary_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("Capsule does not have 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("FMP 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 */
- if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) &&
- !(image->image_capsule_support &
- CAPSULE_SUPPORT_AUTHENTICATION)) {
- /* no signature */
- ret = EFI_SECURITY_VIOLATION;
- goto out;
- }
- image_binary = (void *)image + sizeof(*image);
- image_binary_size = image->update_image_size;
- vendor_code = image_binary + image_binary_size;
- if (!IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) &&
- (image->image_capsule_support &
- CAPSULE_SUPPORT_AUTHENTICATION)) {
- ret = efi_remove_auth_hdr(&image_binary,
- &image_binary_size);
- if (ret != EFI_SUCCESS)
- goto out;
- }
- abort_reason = NULL;
- ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
- image_binary,
- image_binary_size,
- vendor_code, NULL,
- &abort_reason));
- if (ret != EFI_SUCCESS) {
- log_err("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, %zu, %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("Capsule does not have 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("Unsupported capsule type: %pUl\n",
- &capsule->capsule_guid);
- ret = EFI_UNSUPPORTED;
- }
- if (ret != EFI_SUCCESS)
- goto out;
- }
- if (IS_ENABLED(CONFIG_EFI_ESRT)) {
- /* Rebuild the ESRT to reflect any updated FW images. */
- ret = efi_esrt_populate();
- if (ret != EFI_SUCCESS)
- log_warning("ESRT update failed\n");
- }
- 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, %zu, %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 found;
- } 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;
- }
- found:
- if (boot_dev) {
- log_debug("Boot device %pD\n", boot_dev);
- 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;
- }
- out:
- 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("Boot device 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) {
- struct efi_file_info *old_dirent = dirent;
- dirent = realloc(dirent, tmp_size);
- if (!dirent) {
- dirent = old_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))
- count++;
- }
- ret = EFI_CALL((*dirh->setpos)(dirh, 0));
- if (ret != EFI_SUCCESS)
- goto err;
- /* make a list */
- tmp_files = malloc(count * sizeof(*tmp_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));
- if (ret == EFI_SUCCESS)
- 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;
- }
- /**
- * efi_load_capsule_drivers - initialize capsule drivers
- *
- * Generic FMP drivers backed by DFU
- *
- * Return: status code
- */
- efi_status_t __weak 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(
- &handle,
- &efi_guid_firmware_management_protocol,
- &efi_fmp_raw, NULL));
- }
- return ret;
- }
- /**
- * check_run_capsules - Check whether capsule update should run
- *
- * The spec says OsIndications must be set in order to run the capsule update
- * on-disk. Since U-Boot doesn't support runtime SetVariable, allow capsules to
- * run explicitly if CONFIG_EFI_IGNORE_OSINDICATIONS is selected
- */
- static bool check_run_capsules(void)
- {
- u64 os_indications;
- efi_uintn_t size;
- efi_status_t ret;
- if (IS_ENABLED(CONFIG_EFI_IGNORE_OSINDICATIONS))
- return true;
- 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 true;
- return false;
- }
- /**
- * 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)
- {
- struct efi_capsule_header *capsule = NULL;
- u16 **files;
- unsigned int nfiles, index, i;
- efi_status_t ret;
- if (!check_run_capsules())
- return EFI_SUCCESS;
- index = get_last_capsule();
- /*
- * 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("Applying %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("Applying capsule %ls failed\n",
- files[i]);
- free(capsule);
- } else {
- log_err("Reading capsule %ls failed\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("Deleting capsule %ls failed\n",
- files[i]);
- }
- efi_capsule_scan_done();
- for (i = 0; i < nfiles; i++)
- free(files[i]);
- free(files);
- return ret;
- }
- #endif /* CONFIG_EFI_CAPSULE_ON_DISK */
|