|
@@ -15,6 +15,10 @@
|
|
|
#include <sort.h>
|
|
|
|
|
|
const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
|
|
|
+static const efi_guid_t efi_guid_firmware_management_capsule_id =
|
|
|
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
|
|
|
+const efi_guid_t efi_guid_firmware_management_protocol =
|
|
|
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
|
|
|
|
|
|
#ifdef CONFIG_EFI_CAPSULE_ON_DISK
|
|
|
/* for file system access */
|
|
@@ -85,9 +89,214 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule,
|
|
|
EFI_VARIABLE_RUNTIME_ACCESS,
|
|
|
sizeof(result), &result);
|
|
|
if (ret)
|
|
|
- printf("EFI: creating %ls failed\n", variable_name16);
|
|
|
+ log_err("EFI: creating %ls failed\n", variable_name16);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
|
|
|
+/**
|
|
|
+ * efi_fmp_find - search for Firmware Management Protocol drivers
|
|
|
+ * @image_type: Image type guid
|
|
|
+ * @instance: Instance number
|
|
|
+ * @handles: Handles of FMP drivers
|
|
|
+ * @no_handles: Number of handles
|
|
|
+ *
|
|
|
+ * Search for Firmware Management Protocol drivers, matching the image
|
|
|
+ * type, @image_type and the machine instance, @instance, from the list,
|
|
|
+ * @handles.
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * * Protocol instance - on success
|
|
|
+ * * NULL - on failure
|
|
|
+ */
|
|
|
+static struct efi_firmware_management_protocol *
|
|
|
+efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles,
|
|
|
+ efi_uintn_t no_handles)
|
|
|
+{
|
|
|
+ efi_handle_t *handle;
|
|
|
+ struct efi_firmware_management_protocol *fmp;
|
|
|
+ struct efi_firmware_image_descriptor *image_info, *desc;
|
|
|
+ efi_uintn_t info_size, descriptor_size;
|
|
|
+ u32 descriptor_version;
|
|
|
+ u8 descriptor_count;
|
|
|
+ u32 package_version;
|
|
|
+ u16 *package_version_name;
|
|
|
+ bool found = false;
|
|
|
+ int i, j;
|
|
|
+ efi_status_t ret;
|
|
|
+
|
|
|
+ for (i = 0, handle = handles; i < no_handles; i++, handle++) {
|
|
|
+ ret = EFI_CALL(efi_handle_protocol(
|
|
|
+ *handle,
|
|
|
+ &efi_guid_firmware_management_protocol,
|
|
|
+ (void **)&fmp));
|
|
|
+ if (ret != EFI_SUCCESS)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* get device's image info */
|
|
|
+ info_size = 0;
|
|
|
+ image_info = NULL;
|
|
|
+ descriptor_version = 0;
|
|
|
+ descriptor_count = 0;
|
|
|
+ descriptor_size = 0;
|
|
|
+ package_version = 0;
|
|
|
+ package_version_name = NULL;
|
|
|
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
|
|
|
+ image_info,
|
|
|
+ &descriptor_version,
|
|
|
+ &descriptor_count,
|
|
|
+ &descriptor_size,
|
|
|
+ &package_version,
|
|
|
+ &package_version_name));
|
|
|
+ if (ret != EFI_BUFFER_TOO_SMALL)
|
|
|
+ goto skip;
|
|
|
+
|
|
|
+ image_info = malloc(info_size);
|
|
|
+ if (!image_info)
|
|
|
+ goto skip;
|
|
|
+
|
|
|
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
|
|
|
+ image_info,
|
|
|
+ &descriptor_version,
|
|
|
+ &descriptor_count,
|
|
|
+ &descriptor_size,
|
|
|
+ &package_version,
|
|
|
+ &package_version_name));
|
|
|
+ if (ret != EFI_SUCCESS ||
|
|
|
+ descriptor_version != EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION)
|
|
|
+ goto skip;
|
|
|
+
|
|
|
+ /* matching */
|
|
|
+ for (j = 0, desc = image_info; j < descriptor_count;
|
|
|
+ j++, desc = (void *)desc + descriptor_size) {
|
|
|
+ log_debug("+++ desc[%d] index: %d, name: %ls\n",
|
|
|
+ j, desc->image_index, desc->image_id_name);
|
|
|
+ if (!guidcmp(&desc->image_type_id, image_type) &&
|
|
|
+ (!instance ||
|
|
|
+ !desc->hardware_instance ||
|
|
|
+ desc->hardware_instance == instance))
|
|
|
+ found = true;
|
|
|
+ }
|
|
|
+
|
|
|
+skip:
|
|
|
+ efi_free_pool(package_version_name);
|
|
|
+ free(image_info);
|
|
|
+ EFI_CALL(efi_close_protocol(
|
|
|
+ (efi_handle_t)fmp,
|
|
|
+ &efi_guid_firmware_management_protocol,
|
|
|
+ NULL, NULL));
|
|
|
+ if (found)
|
|
|
+ return fmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * efi_capsule_update_firmware - update firmware from capsule
|
|
|
+ * @capsule_data: Capsule
|
|
|
+ *
|
|
|
+ * Update firmware, using a capsule, @capsule_data. Loading any FMP
|
|
|
+ * drivers embedded in a capsule is not supported.
|
|
|
+ *
|
|
|
+ * Return: status code
|
|
|
+ */
|
|
|
+static efi_status_t efi_capsule_update_firmware(
|
|
|
+ struct efi_capsule_header *capsule_data)
|
|
|
+{
|
|
|
+ struct efi_firmware_management_capsule_header *capsule;
|
|
|
+ struct efi_firmware_management_capsule_image_header *image;
|
|
|
+ size_t capsule_size;
|
|
|
+ void *image_binary, *vendor_code;
|
|
|
+ efi_handle_t *handles;
|
|
|
+ efi_uintn_t no_handles;
|
|
|
+ int item;
|
|
|
+ struct efi_firmware_management_protocol *fmp;
|
|
|
+ u16 *abort_reason;
|
|
|
+ efi_status_t ret = EFI_SUCCESS;
|
|
|
+
|
|
|
+ /* sanity check */
|
|
|
+ if (capsule_data->header_size < sizeof(*capsule) ||
|
|
|
+ capsule_data->header_size >= capsule_data->capsule_image_size)
|
|
|
+ return EFI_INVALID_PARAMETER;
|
|
|
+
|
|
|
+ capsule = (void *)capsule_data + capsule_data->header_size;
|
|
|
+ capsule_size = capsule_data->capsule_image_size
|
|
|
+ - capsule_data->header_size;
|
|
|
+
|
|
|
+ if (capsule->version != 0x00000001)
|
|
|
+ return EFI_UNSUPPORTED;
|
|
|
+
|
|
|
+ handles = NULL;
|
|
|
+ ret = EFI_CALL(efi_locate_handle_buffer(
|
|
|
+ BY_PROTOCOL,
|
|
|
+ &efi_guid_firmware_management_protocol,
|
|
|
+ NULL, &no_handles, (efi_handle_t **)&handles));
|
|
|
+ if (ret != EFI_SUCCESS)
|
|
|
+ return EFI_UNSUPPORTED;
|
|
|
+
|
|
|
+ /* Payload */
|
|
|
+ for (item = capsule->embedded_driver_count;
|
|
|
+ item < capsule->embedded_driver_count
|
|
|
+ + capsule->payload_item_count; item++) {
|
|
|
+ /* sanity check */
|
|
|
+ if ((capsule->item_offset_list[item] + sizeof(*image)
|
|
|
+ >= capsule_size)) {
|
|
|
+ log_err("EFI: A capsule has not enough data\n");
|
|
|
+ ret = EFI_INVALID_PARAMETER;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ image = (void *)capsule + capsule->item_offset_list[item];
|
|
|
+
|
|
|
+ if (image->version != 0x00000003) {
|
|
|
+ ret = EFI_UNSUPPORTED;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* find a device for update firmware */
|
|
|
+ /* TODO: should we pass index as well, or nothing but type? */
|
|
|
+ fmp = efi_fmp_find(&image->update_image_type_id,
|
|
|
+ image->update_hardware_instance,
|
|
|
+ handles, no_handles);
|
|
|
+ if (!fmp) {
|
|
|
+ log_err("EFI Capsule: driver not found for firmware type: %pUl, hardware instance: %lld\n",
|
|
|
+ &image->update_image_type_id,
|
|
|
+ image->update_hardware_instance);
|
|
|
+ ret = EFI_UNSUPPORTED;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* do update */
|
|
|
+ image_binary = (void *)image + sizeof(*image);
|
|
|
+ vendor_code = image_binary + image->update_image_size;
|
|
|
+
|
|
|
+ abort_reason = NULL;
|
|
|
+ ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
|
|
|
+ image_binary,
|
|
|
+ image->update_image_size,
|
|
|
+ vendor_code, NULL,
|
|
|
+ &abort_reason));
|
|
|
+ if (ret != EFI_SUCCESS) {
|
|
|
+ log_err("EFI Capsule: firmware update failed: %ls\n",
|
|
|
+ abort_reason);
|
|
|
+ efi_free_pool(abort_reason);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ efi_free_pool(handles);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#else
|
|
|
+static efi_status_t efi_capsule_update_firmware(
|
|
|
+ struct efi_capsule_header *capsule_data)
|
|
|
+{
|
|
|
+ return EFI_UNSUPPORTED;
|
|
|
+}
|
|
|
+#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */
|
|
|
+
|
|
|
/**
|
|
|
* efi_update_capsule() - process information from operating system
|
|
|
* @capsule_header_array: Array of virtual address pointers
|
|
@@ -118,9 +327,29 @@ efi_status_t EFIAPI efi_update_capsule(
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- ret = EFI_UNSUPPORTED;
|
|
|
+ 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);
|
|
@@ -265,7 +494,7 @@ static efi_status_t find_boot_device(void)
|
|
|
if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
|
|
|
/* BootNext does exist here */
|
|
|
if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
|
|
|
- printf("BootNext must be 16-bit integer\n");
|
|
|
+ log_err("BootNext must be 16-bit integer\n");
|
|
|
goto skip;
|
|
|
}
|
|
|
sprintf((char *)boot_var, "Boot%04X", bootnext);
|
|
@@ -323,7 +552,7 @@ out:
|
|
|
u16 *path_str;
|
|
|
|
|
|
path_str = efi_dp_str(boot_dev);
|
|
|
- EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str);
|
|
|
+ log_debug("EFI Capsule: bootdev is %ls\n", path_str);
|
|
|
efi_free_pool(path_str);
|
|
|
|
|
|
volume = efi_fs_from_path(boot_dev);
|
|
@@ -363,7 +592,7 @@ static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num)
|
|
|
|
|
|
ret = find_boot_device();
|
|
|
if (ret == EFI_NOT_FOUND) {
|
|
|
- EFI_PRINT("EFI Capsule: bootdev is not set\n");
|
|
|
+ log_debug("EFI Capsule: bootdev is not set\n");
|
|
|
*num = 0;
|
|
|
return EFI_SUCCESS;
|
|
|
} else if (ret != EFI_SUCCESS) {
|
|
@@ -619,20 +848,19 @@ efi_status_t efi_launch_capsules(void)
|
|
|
|
|
|
/* Launch capsules */
|
|
|
for (i = 0, ++index; i < nfiles; i++, index++) {
|
|
|
- EFI_PRINT("capsule from %ls ...\n", files[i]);
|
|
|
+ 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)
|
|
|
- printf("EFI Capsule update failed at %ls\n",
|
|
|
- files[i]);
|
|
|
+ log_err("EFI Capsule update failed at %ls\n",
|
|
|
+ files[i]);
|
|
|
|
|
|
free(capsule);
|
|
|
} else {
|
|
|
- printf("EFI: reading capsule failed: %ls\n",
|
|
|
- files[i]);
|
|
|
+ log_err("EFI: reading capsule failed: %ls\n", files[i]);
|
|
|
}
|
|
|
/* create CapsuleXXXX */
|
|
|
set_capsule_result(index, capsule, ret);
|
|
@@ -640,8 +868,8 @@ efi_status_t efi_launch_capsules(void)
|
|
|
/* delete a capsule either in case of success or failure */
|
|
|
ret = efi_capsule_delete_file(files[i]);
|
|
|
if (ret != EFI_SUCCESS)
|
|
|
- printf("EFI: deleting a capsule file failed: %ls\n",
|
|
|
- files[i]);
|
|
|
+ log_err("EFI: deleting a capsule file failed: %ls\n",
|
|
|
+ files[i]);
|
|
|
}
|
|
|
efi_capsule_scan_done();
|
|
|
|