ci(esp_tee): Enable the tee_test_fw test app for ESP32-H2

This commit is contained in:
Laukik Hase
2025-05-16 12:16:57 +05:30
parent 27496e47f0
commit d482206483
5 changed files with 195 additions and 107 deletions

View File

@ -7,5 +7,5 @@ components/esp_tee/test_apps/tee_cli_app:
components/esp_tee/test_apps/tee_test_fw:
disable:
- if: IDF_TARGET not in ["esp32c6"]
reason: only supported with esp32c6 for now
- if: IDF_TARGET not in ["esp32c6", "esp32h2"]
reason: only supported with esp32c6 and esp32h2 for now

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## ESP-TEE: Test Suite

View File

@ -63,6 +63,47 @@ esp_tee_empty_bin = {
0xF4, 0xA4, 0xCF, 0x06, 0xAE, 0x94, 0x75, 0x47, 0xBC, 0x88, 0xA2, 0xCA,
0x52, 0x97, 0x7A, 0x5C, 0x55, 0x43, 0xD9, 0xF5, 0xD3, 0x45, 0xD1, 0x34,
0xFC, 0x74, 0xB2, 0xB9, 0x34, 0x72, 0xC3, 0x00
],
'esp32h2': [
0xE9, 0x04, 0x02, 0x1F, 0x00, 0x00, 0x80, 0x40, 0xEE, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# DROM segment
0x20, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x00,
# esp_app_desc structure
0x32, 0x54, 0xCD, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x65, 0x73, 0x70, 0x5F, 0x74, 0x65, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4E, 0x6F, 0x76, 0x20, 0x31, 0x31, 0x20, 0x32,
0x30, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2D, 0x63, 0x66, 0x8B, 0x75, 0xFA, 0x59, 0x05,
0x53, 0x34, 0x91, 0x71, 0x51, 0x33, 0x91, 0xDD, 0xF8, 0xB1, 0xFE, 0x83,
0x06, 0xEB, 0x03, 0x80, 0x45, 0xC9, 0x18, 0x20, 0x83, 0x7E, 0x2E, 0x43,
*([0x00] * 0x58),
# Padding
*([0x00] * 0x100),
# IRAM segment
0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00,
*([0x00] * 0x20),
# PADDING segment
0x00, 0x00, 0x00, 0x00, 0xC8, 0x7D, 0x00, 0x00,
*([0x00] * 0x7DC8),
# IROM segment
0x20, 0x80, 0x00, 0x42, 0x00, 0x01, 0x00, 0x00,
*([0x00] * 0x100),
# Padding
*([0x00] * 0x0F),
# CRC8 checksum
0x56,
# Image SHA256
0xDC, 0x60, 0x86, 0x6C, 0x37, 0x76, 0xAA, 0x30, 0x1F, 0x61, 0x48, 0x23,
0xEA, 0x34, 0xAA, 0x19, 0xE8, 0xDE, 0x04, 0x7D, 0x2A, 0x30, 0xC1, 0xDD,
0x61, 0x38, 0x9D, 0xB5, 0xCA, 0x13, 0x5A, 0x79
]
}
# fmt: on

View File

@ -119,7 +119,7 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1)", "[sec_stora
}
}
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN && !TEMPORARY_DISABLED_FOR_TARGETS(ESP32H2)
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1)", "[sec_storage]")
{
const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size
@ -507,7 +507,7 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid)
TEST_CASE("Test TEE Secure Storage - mbedtls ECDSA signing", "[mbedtls]")
{
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1);
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN && !TEMPORARY_DISABLED_FOR_TARGETS(ESP32H2)
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1);
#endif
}

View File

@ -1,42 +1,57 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Any
from typing import Dict
from typing import Tuple
import pytest
from pytest_embedded_idf import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
CONFIGS_DEFAULT = [pytest.param('default', marks=[pytest.mark.esp32c6])]
# ---------------- Pytest build parameters ----------------
CONFIGS_OTA = [pytest.param('ota', marks=[pytest.mark.esp32c6])]
SUPPORTED_TARGETS = ['esp32c6', 'esp32h2']
CONFIGS_ALL = [pytest.param('default', marks=[pytest.mark.esp32c6]), pytest.param('ota', marks=[pytest.mark.esp32c6])]
CONFIG_DEFAULT = [
# 'config, target, markers',
('default', target, (pytest.mark.generic,))
for target in SUPPORTED_TARGETS
]
TEE_VIOLATION_TEST_EXC_RSN: Dict[str, Any] = {
'esp32c6': {
('Reserved', 'W1'): 'Store access fault',
('Reserved', 'X1'): 'Instruction access fault',
('IRAM', 'W1'): 'Store access fault',
('IRAM', 'W2'): 'Store access fault',
('DRAM', 'X1'): 'Instruction access fault',
('DRAM', 'X2'): 'Instruction access fault',
}
CONFIG_OTA = [
# 'config, target, skip_autoflash, markers',
('ota', target, 'y', (pytest.mark.generic,))
for target in SUPPORTED_TARGETS
]
CONFIG_ALL = [
# 'config, target, markers',
(config, target, (pytest.mark.generic,))
for config in ['default', 'ota']
for target in SUPPORTED_TARGETS
]
# ---------------- Exception test-cases reasons ----------------
TEE_VIOLATION_TEST_EXC_RSN: Dict[Tuple[str, str], str] = {
('Reserved', 'W1'): 'Store access fault',
('Reserved', 'X1'): 'Instruction access fault',
('IRAM', 'W1'): 'Store access fault',
('IRAM', 'W2'): 'Store access fault',
('DRAM', 'X1'): 'Instruction access fault',
('DRAM', 'X2'): 'Instruction access fault',
}
REE_ISOLATION_TEST_EXC_RSN: Dict[str, Any] = {
'esp32c6': {
('DRAM', 'R1'): 'Load access fault',
('DRAM', 'W1'): 'Store access fault',
('IRAM', 'R1'): 'Load access fault',
('IRAM', 'W1'): 'Store access fault',
('IROM', 'R1'): 'Load access fault',
('IROM', 'W1'): 'Store access fault',
('DROM', 'R1'): 'Load access fault',
('DROM', 'W1'): 'Store access fault',
('SWDT/BOD', 'W'): 'Store access fault',
}
REE_ISOLATION_TEST_EXC_RSN: Dict[Tuple[str, str], str] = {
('DRAM', 'R1'): 'Load access fault',
('DRAM', 'W1'): 'Store access fault',
('IRAM', 'R1'): 'Load access fault',
('IRAM', 'W1'): 'Store access fault',
('IROM', 'R1'): 'Load access fault',
('IROM', 'W1'): 'Store access fault',
('DROM', 'R1'): 'Load access fault',
('DROM', 'W1'): 'Store access fault',
('SWDT/BOD', 'W'): 'Store access fault',
}
TEE_APM_VIOLATION_EXC_CHK = ['eFuse', 'MMU', 'AES', 'HMAC', 'DS', 'SHA PCR', 'ECC PCR']
@ -44,34 +59,42 @@ TEE_APM_VIOLATION_EXC_CHK = ['eFuse', 'MMU', 'AES', 'HMAC', 'DS', 'SHA PCR', 'EC
# ---------------- TEE default tests ----------------
@pytest.mark.generic
@idf_parametrize('config', ['default'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='basic')
dut.run_all_single_board_cases(group='heap')
@pytest.mark.generic
@idf_parametrize('config', ['default', 'ota'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_ALL,
indirect=['config', 'target'],
)
def test_esp_tee_crypto_aes(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='aes')
dut.run_all_single_board_cases(group='aes-gcm')
@pytest.mark.generic
@idf_parametrize('config', ['default', 'ota'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_ALL,
indirect=['config', 'target'],
)
def test_esp_tee_crypto_sha(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='mbedtls')
dut.run_all_single_board_cases(group='hw_crypto')
# NOTE: Stress testing the AES performance case for interrupt-related edge-cases
@pytest.mark.generic
@idf_parametrize('config', ['default', 'ota'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_ALL,
indirect=['config', 'target'],
)
def test_esp_tee_aes_perf(dut: IdfDut) -> None:
# start test
for i in range(24):
@ -86,21 +109,29 @@ def test_esp_tee_aes_perf(dut: IdfDut) -> None:
# ---------------- TEE Exceptions generation Tests ----------------
@pytest.mark.generic
@idf_parametrize('config', ['default'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_apm_violation(dut: IdfDut) -> None:
for check in TEE_APM_VIOLATION_EXC_CHK:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write(f'"Test APM violation interrupt: {check}"')
exc = dut.expect(r'Core ([01]) panic\'ed \(([^)]+)\)', timeout=30).group(2).decode()
if exc != 'APM - Authority exception':
if dut.target == 'esp32h2' and check == 'eFuse':
exp_str = 'APM - Space exception'
else:
exp_str = 'APM - Authority exception'
if exc != exp_str:
raise RuntimeError('Incorrect exception received!')
@pytest.mark.generic
@idf_parametrize('config', ['default'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_illegal_instruction(dut: IdfDut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('"Test TEE-TEE violation: Illegal Instruction"')
@ -109,11 +140,13 @@ def test_esp_tee_illegal_instruction(dut: IdfDut) -> None:
raise RuntimeError('Incorrect exception received!')
@pytest.mark.generic
@idf_parametrize('config', ['default'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_violation_checks(dut: IdfDut) -> None:
checks_list = TEE_VIOLATION_TEST_EXC_RSN[dut.target]
checks_list = TEE_VIOLATION_TEST_EXC_RSN
for test in checks_list:
memory, access_type = test
expected_exc = checks_list[test]
@ -126,11 +159,13 @@ def test_esp_tee_violation_checks(dut: IdfDut) -> None:
raise RuntimeError('Incorrect exception received!')
@pytest.mark.generic
@idf_parametrize('config', ['default'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_isolation_checks(dut: IdfDut) -> None:
checks_list = REE_ISOLATION_TEST_EXC_RSN[dut.target]
checks_list = REE_ISOLATION_TEST_EXC_RSN
for test in checks_list:
memory, access_type = test
expected_exc = checks_list[test]
@ -199,10 +234,11 @@ def run_multiple_stages(dut: IdfDut, test_case_num: int, stages: int, api: TeeFl
dut.expect_exact('Press ENTER to see the list of tests.')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_flash_prot_esp_partition_mmap(dut: IdfDut) -> None:
# Flash the bootloader, TEE and REE firmware
dut.serial.custom_flash()
@ -216,10 +252,11 @@ def test_esp_tee_flash_prot_esp_partition_mmap(dut: IdfDut) -> None:
continue
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_flash_prot_spi_flash_mmap(dut: IdfDut) -> None:
# Flash the bootloader, TEE and REE firmware
dut.serial.custom_flash()
@ -233,10 +270,11 @@ def test_esp_tee_flash_prot_spi_flash_mmap(dut: IdfDut) -> None:
continue
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_flash_prot_esp_rom_spiflash(dut: IdfDut) -> None:
# Flash the bootloader, TEE and REE firmware
dut.serial.custom_flash()
@ -250,10 +288,11 @@ def test_esp_tee_flash_prot_esp_rom_spiflash(dut: IdfDut) -> None:
continue
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_flash_prot_esp_partition(dut: IdfDut) -> None:
# Flash the bootloader, TEE and REE firmware
dut.serial.custom_flash()
@ -267,10 +306,11 @@ def test_esp_tee_flash_prot_esp_partition(dut: IdfDut) -> None:
continue
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_flash_prot_esp_flash(dut: IdfDut) -> None:
# Flash the bootloader, TEE and REE firmware
dut.serial.custom_flash()
@ -289,7 +329,7 @@ def test_esp_tee_flash_prot_esp_flash(dut: IdfDut) -> None:
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize('target', ['esp32c6', 'esp32h2'], indirect=['target'])
def test_esp_tee_ota_negative(dut: IdfDut) -> None:
# start test
dut.expect_exact('Press ENTER to see the list of tests')
@ -300,10 +340,11 @@ def test_esp_tee_ota_negative(dut: IdfDut) -> None:
dut.serial.erase_partition('tee_otadata')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_ota_corrupted_img(dut: IdfDut) -> None:
# Flashing the TEE app to the non-secure app's passive partition
dut.serial.custom_flash_w_test_tee_img_gen()
@ -338,10 +379,11 @@ def tee_ota_stage_checks(dut: IdfDut, stage: TeeOtaStage, offset: str) -> None:
raise ValueError('Undefined stage!')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None:
# Flashing the TEE app to the non-secure app's passive partition
dut.serial.custom_flash_w_test_tee_img_gen()
@ -363,10 +405,11 @@ def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None:
dut.serial.erase_partition('tee_otadata')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_ota_valid_img(dut: IdfDut) -> None:
# Flashing the TEE app to the non-secure app's passive partition
dut.serial.custom_flash_w_test_tee_img_gen()
@ -396,10 +439,11 @@ def test_esp_tee_ota_valid_img(dut: IdfDut) -> None:
dut.serial.erase_partition('tee_otadata')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_ota_rollback(dut: IdfDut) -> None:
# Flashing the TEE app to the non-secure app's passive partition
dut.serial.custom_flash_w_test_tee_img_rb()
@ -434,10 +478,11 @@ def test_esp_tee_ota_rollback(dut: IdfDut) -> None:
# ---------------- TEE Secure Storage tests ----------------
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_secure_storage(dut: IdfDut) -> None:
# Flash image and erase the secure_storage partition
dut.serial.custom_flash_with_empty_sec_stg()
@ -445,10 +490,11 @@ def test_esp_tee_secure_storage(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='sec_storage')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_secure_storage_with_host_img(dut: IdfDut) -> None:
# Flash image and write the secure_storage partition with host-generated keys
dut.serial.custom_flash_with_host_gen_sec_stg_img()
@ -459,10 +505,11 @@ def test_esp_tee_secure_storage_with_host_img(dut: IdfDut) -> None:
# ---------------- TEE Attestation tests ----------------
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
@idf_parametrize(
'config, target, skip_autoflash, markers',
CONFIG_OTA,
indirect=['config', 'target', 'skip_autoflash'],
)
def test_esp_tee_attestation(dut: IdfDut) -> None:
# Flash image and erase the secure_storage partition
dut.serial.custom_flash_with_empty_sec_stg()