Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regarding Slow Hash Computation of Running Partition on ESP32-C2 (IDFGH-14435) #15215

Closed
3 tasks done
aggaddam opened this issue Jan 15, 2025 · 5 comments
Closed
3 tasks done
Assignees
Labels
Awaiting Response awaiting a response from the author Status: Opened Issue is new

Comments

@aggaddam
Copy link

aggaddam commented Jan 15, 2025

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

I am currently working on an ESP32-C2 target and have observed an issue with hash computation performance. Specifically, computing the SHA256 hash of the running partition (size 1MB) takes approximately 40 to 60 seconds due to the absence of a DMA engine for SPI flash. However, the same hash computation performs efficiently during the bootloader's secure boot signature validation process when software secure boot is enabled using the SDK configuration.

For reference, I have attached the pseudo code I am using in my application. This implementation works well on other ESP32 targets, but on ESP32-C2 (Chip rev 1, IDF v5.1), the computation takes significantly longer. I also tried using the bootloader API, bootloader_sha256_flash_contents(), to compute the hash, but it did not result in a noticeable improvement in execution time.

pseudo code:

`bool device_compute_fw_hash(uint32_t fw_size,
uint8_t *hash_buff, uint16_t hash_buff_len)
{
bool ret_val = false;
uint8_t rd_buff[1024] = {0};

ESP_LOGI(TAG, "Computing FW Hash,FW sz = %"PRIu32"", fw_size);

if ((NULL != hash_buff) && (hash_buff_len >= SHA256_DIGEST_SZ))
{
    // Compute hash of firmware binary
    const esp_partition_t *running = esp_ota_get_running_partition();
    if (NULL != running)
    {
        ESP_LOGD(TAG, "Part Addr = 0x%"PRIx32"", running->address);
        ESP_LOGD(TAG, "Part Sz   = 0x%"PRIx32"", running->size);
        ESP_LOGD(TAG, "Part Type = [%s,%d,%d]", running->label, running->type, running->subtype);

        if (fw_size <= running->size)
        {
            uint32_t addr_offset        = 0;
            uint32_t n_blocks           = fw_size/sizeof(rd_buff);;
            uint32_t bytes_left_to_read = fw_size % sizeof(rd_buff);

            ESP_LOGD(TAG, "Blocks to rd     = %"PRIu32"", n_blocks);
            ESP_LOGD(TAG, "Bytes left to rd = %"PRIu32"", bytes_left_to_read);

            mbedtls_sha256_context sha256_hdl;
            mbedtls_sha256_init(&sha256_hdl);
            mbedtls_sha256_starts(&sha256_hdl, false); // false - SHA-256, true - SHA-224

            for (uint32_t i = 0; i < n_blocks; i++)
            {
                esp_partition_read(running, addr_offset, (void *)rd_buff, sizeof(rd_buff));
                mbedtls_sha256_update(&sha256_hdl, (const uint8_t *)rd_buff, sizeof(rd_buff));
                addr_offset += sizeof(rd_buff);
            }

            if (bytes_left_to_read > 0)
            {
                esp_partition_read(running, addr_offset, (void *)rd_buff, bytes_left_to_read);
                mbedtls_sha256_update(&sha256_hdl, (const uint8_t *)rd_buff, bytes_left_to_read);
            }

            mbedtls_sha256_finish(&sha256_hdl, hash_buff);
            mbedtls_sha256_free(&sha256_hdl);
            ret_val = true;
        }
        else
        {
            ESP_LOGE(TAG, "Invalid FW SZ");
        }
    }
}
return (ret_val);

}
`

I would like to understand how the SHA256 computation is implemented in the bootloader during secure boot validation. Are there any optimizations or differences in the approach compared to hash computation on the running partition? Additionally, could you please provide recommendations or configurations to improve the hash computation speed for the running partition?

@espressif-bot espressif-bot added the Status: Opened Issue is new label Jan 15, 2025
@github-actions github-actions bot changed the title Regarding Slow Hash Computation of Running Partition on ESP32-C2 Regarding Slow Hash Computation of Running Partition on ESP32-C2 (IDFGH-14435) Jan 15, 2025
@igrr
Copy link
Member

igrr commented Jan 15, 2025

The main difference is that the bootloader maps the application image into CPU address space using Flash MMU, and then reads the parts of the binary via the MMU.

In your implementation, every call to esp_partition_read has a significant overhead, as Flash reads (which aren't done via the MMU) require preemption of all the code in the system which runs from Flash.

I would recommend checking esp_partition_mmap function. You can map one 64kB page at a time, calculate the hash, unmap, map the next one, etc. This should work faster than your current approach.

@aggaddam
Copy link
Author

I tried bootloader_sha256_flash_contents() and also with esp_partition_mmap() which uses Flash MMU but still hash computation time is high ~30 sec to 35 sec for the running partition in firmware application

@igrr
Copy link
Member

igrr commented Jan 15, 2025

Going a bit back to the original problem, could you please clarify the need for computing the hash of the running partition?

You can query the hash appended to the image itself, without re-calculating it — since it is verified when starting the app:

#include "esp_log.h"
#include "esp_image_format.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"

static const char *TAG = "example";

void app_main(void)
{
    const esp_partition_t* running = esp_ota_get_running_partition();
    const esp_partition_pos_t part_pos = {
        .offset = running->address,
        .size = running->size,
    };
    esp_image_metadata_t img_metadata = {0};
    ESP_ERROR_CHECK(esp_image_get_metadata(&part_pos, &img_metadata));
    ESP_LOGI(TAG, "Image SHA256 hash:");
    ESP_LOG_BUFFER_HEX(TAG, img_metadata.image_digest, sizeof(img_metadata.image_digest));
}

@AdityaHPatwardhan AdityaHPatwardhan added the Awaiting Response awaiting a response from the author label Jan 20, 2025
@AdityaHPatwardhan AdityaHPatwardhan self-assigned this Jan 20, 2025
@mahavirj
Copy link
Member

@aggaddam

For C2 case, we have a SHA performance test here, you may try running it. Performance benchmark with hardware acceleration enabled case is set to to 14 MB/sec (reference). Even with hardware acceleration disabled case, performance should be close to 2-3 MB/sec.

As Ivan mentioned, problem could be how the data is read and supplied to SHA module. Only difference w.r.t. bootloader that I can think of is the code execution from internal memory vs. external flash (XIP).

Using API esp_image_get_metadata should work unless you have an use-case like attestation where the hash must be re-calculated at runtime. Please let us know on the requirement and then we can discuss further.

@aggaddam
Copy link
Author

I figured out that it was not an issue with hash computation.I had an issue with my application code. So closing this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Response awaiting a response from the author Status: Opened Issue is new
Projects
None yet
Development

No branches or pull requests

5 participants